Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:122852 X-Original-To: internals@lists.php.net Delivered-To: internals@lists.php.net Received: from php-smtp4.php.net (php-smtp4.php.net [45.112.84.5]) by qa.php.net (Postfix) with ESMTPS id 4DCCA1A009C for ; Tue, 2 Apr 2024 10:12:06 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1712052754; bh=27GuSDNZcprg3JsaiSFQ7lFuo2c4uz8tXvxbt2Joec8=; h=References:In-Reply-To:From:Date:Subject:To:Cc:From; b=LnMj6gjCoFMRpA1lWOjkwDZo/Ftq+6Zh0kN2vZ1N3HyJPLu0drMJU6YCdUPuHbQzT 0YgX5jzmLG8KDrOL6873KcRvhA9aDN4Nfa4y8i+X0BA7j0hzhShAYJ992oyM7w6xzJ swU8EjjqCa/n2ZNC80cnytv9dotF+cl/X1VkpKhTjBmNCFpWYuldOg6gWsQ8q70WYr n3FLvxJTluwq6K115CANuaM/ola+0lXjR/3gOBdeqDBug8bxvjHhbit/TxcyQSd8rm +UmhPEVRYO5N2GRIjA0ZQi99+lVz4lHLPA2cDMxmxw5YCm1pZwj9uhz79GamhoP2Fm eJfCpW+W1jiVQ== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 024D41801EE for ; Tue, 2 Apr 2024 10:12:33 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 4.0.0 (2022-12-13) on php-smtp4.php.net X-Spam-Level: X-Spam-Status: No, score=0.6 required=5.0 tests=BAYES_50,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_PASS,FREEMAIL_FROM, HTML_MESSAGE,RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H2,SPF_HELO_NONE, SPF_PASS,T_SCC_BODY_TEXT_LINE autolearn=no autolearn_force=no version=4.0.0 X-Spam-Virus: No X-Envelope-From: Received: from mail-lf1-f52.google.com (mail-lf1-f52.google.com [209.85.167.52]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by php-smtp4.php.net (Postfix) with ESMTPS for ; Tue, 2 Apr 2024 10:12:32 +0000 (UTC) Received: by mail-lf1-f52.google.com with SMTP id 2adb3069b0e04-513e134f73aso6402617e87.2 for ; Tue, 02 Apr 2024 03:12:04 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1712052723; x=1712657523; darn=lists.php.net; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:from:to:cc:subject:date:message-id:reply-to; bh=27GuSDNZcprg3JsaiSFQ7lFuo2c4uz8tXvxbt2Joec8=; b=hC3I2SpDGnslwDTSD0bXyjekGXzzcUKNBAFM5OGJX62derBOjfPnTo5Dw28JYov6Nj dqMfLnBPwkf2aNJlD8xunMXdCwnct+wQ3E+rbuHX/8zuoq1yFGvA7D02RZpO8oOiCWXL Nde/GhdHQbikyl/a7uWcdZQYvp6L+jVF254JnwsdjVQamYriibTEZwtxtSyygaXb2fQv pIeFLTajbAp/woZehy9ckvUBX7YflCQy0Pt0jgJr4LnMnQpCxNWvg/8TE1JVbLRZA6F8 VlYaPWm5cmidYK3MfRRmC0DeW7gYEA97Ba++yOk9Rcx3DjKCyv2go6zqNqJlyWcFZXrK Ma0w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1712052723; x=1712657523; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=27GuSDNZcprg3JsaiSFQ7lFuo2c4uz8tXvxbt2Joec8=; b=kaJuZIPIcE53ZTt05PJ+QNMZ+fsLh+pYUBO7/FxY/dXN0JCfe4TJLgzsmry37WfvsE NNJ9xhRBhKPFuyvIULTHisVRovv1qf/oJNF54xN3LnuyEnvqtszVT1mm0r8vcFq2axDl pX3wJXsJd+d9DWzqMcZFm4tdxivqfCLIfhILvRcjkSlB6JTuSUduL+QzgUKkuMJ1N1im voD+uioE518Ls1Sz8nvO82si8XvJRd5uXwEGbBplfx10cDYtUVu2nRrLmgQvwhCwP4dS nQzDRJj0gjNUDYsGTHcC/4KD4Y3CgLlw1Ujg3GkwxWY3YO/QLnkafzYJI4p/2bvbFZ/t iCVg== X-Forwarded-Encrypted: i=1; AJvYcCWOU9NACFRWvMoS8A5ZCX20ivvfF5/yFzMEkCDQxxMstHXkeeW/MTERPQXcv659Ltv987zgnYSrkMC9+Ob0OdniEAGWjN7+wA== X-Gm-Message-State: AOJu0YzlNUC4nQyhhslNBgjHVKDhAoSnu2RqVJR5rwwrZEMrQi8nGK7G 1fX2umMbFmK3xk6+Ad2qZHtVSmGYax5rsP+cQJdnHFOe1AVelYfbzIRh2e1njI9+QqgkZYIOTun 7sU3JaGfUQ86FdPpMdmDOmwoNPTc= X-Google-Smtp-Source: AGHT+IHMkLI0lYuSFdQ6LQ6tRMi2wSajFuo2gGKkH4nxFup2Vr6c59Oxi5B/mYbfXbdNsv7NvLTK8sGBuF5h7cbA9Es= X-Received: by 2002:ac2:5e9c:0:b0:513:c4d9:a0d9 with SMTP id b28-20020ac25e9c000000b00513c4d9a0d9mr9273013lfq.22.1712052722328; Tue, 02 Apr 2024 03:12:02 -0700 (PDT) Precedence: bulk list-help: list-post: List-Id: internals.lists.php.net MIME-Version: 1.0 References: <4F094EDA-5058-407D-AF39-06FD934FDE1F@sakiot.com> <68CF373E-6ABF-4471-8992-813B4BA1B508@sakiot.com> <904197f4-afb5-401e-9e17-7a655c5449d0@alec.pl> In-Reply-To: Date: Tue, 2 Apr 2024 12:11:36 +0200 Message-ID: Subject: Re: [PHP-DEV] [RFC] [Discussion] Support object type in BCMath To: Jordan LeDoux Cc: Saki Takamachi , Aleksander Machniak , php internals Content-Type: multipart/alternative; boundary="00000000000059f23206151a5630" From: kjarli@gmail.com (Lynn) --00000000000059f23206151a5630 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable On Tue, Apr 2, 2024 at 11:17=E2=80=AFAM Jordan LeDoux wrote: > > > On Sat, Mar 30, 2024 at 5:09=E2=80=AFPM Saki Takamachi = wrote: > >> Hi Jordan, >> >> Your opinion may be reasonable given the original BCMath calculation >> order. That is, do you intend code like this? >> >> Signature: >> ``` >> // public function __construct(string|int $number) >> // public function getNumber(?int $scale =3D null): string >> ``` >> >> Add: >> ``` >> // public function add(Number|string|int $number): string >> >> $num =3D new Number('1.23456'); >> $num2 =3D new Number('1.23'); >> >> $add =3D $num + $num2; >> $add->getNumber(); // '2.46456' >> $add->getNumber(1); // =E2=80=982.4' >> >> $add =3D $num->add($num2); >> $add->getNumber(); // '2.46456' >> $add->getNumber(1); // '2.4' >> ``` >> >> Div: >> ``` >> // public function div(Number|string|int $number, int >> $scaleExpansionLimit =3D 10): string >> >> >> // case 1 >> $num =3D new Number('0.0001'); >> $num2 =3D new Number('3'); >> >> $div =3D $num / $num2; // scale expansion limit is always 10 >> $div->getNumber(); // '0.0000333333333' >> >> $div =3D $num->div($num2, 20); >> $div->getNumber(); // '0.00003333333333333333333' >> $div->getNumber(7); // =E2=80=980.0000333' >> >> >> // case 2 >> $num =3D new Number('1.111111'); >> $num2 =3D new Number('3'); >> >> $div =3D $num->div($num2, 3); >> $div->getNumber(); // '0.370' >> $div->getNumber(7); // =E2=80=980.3700000' >> ``` >> >> Since the scale can be inferred for everything other than div, a special >> argument is given only for div. >> >> Regards. >> >> Saki > > > Something like the signature for `getNumber()` in this example would be a > decent solution. Operations which have ambiguous scale (of which truly on= ly > div is in the BCMath library) should *require* scale in the method that > calls the calculation, however for consistency I can certainly see the > argument for requiring it for all calculation methods. The issue is how y= ou > want to handle that for operator overloads, since you cannot provide > arguments in that situation. > > Probably the most sensible way (and I think the way I handled it as well > in my library) is to look at both the left and right operand, grab the > calculated scale of the input for both (or the set scale if the scale has > been manually set), and then calculate with a higher scale. If internally > it produces a rounded result, the calculation should be done at > `$desireScale + 2` to avoid compound rounding errors from the BCMath > library and then the implementation. If the result is truncated, the > calculation should be done at `$desiredScale + 1` to avoid calculating > unnecessary digits. > > So we have multiple usage scenarios and the behavior needs to remain > consistent no matter which usage occurs, and what order the items are > called in, so long as the resulting calculation is the same. > > **Method Call** > $bcNum =3D new Number('1.0394567'); // Input scale is implicitly 7 > $bcNum->div('1.2534', 3); // Resulting scale is 3 > $bcNum->div('1.2534'); // Implicit scale of denominator is 4, Implicit > scale of numerator is 7, calculate with scale of 8 then truncate > > **Operators** > $bcNum =3D new Number('1.0394567'); // Input scale is implicitly 7 > $bcNum / '1.2534'; // Implicit scale of denominator is 4, Implicit scale > of numerator is 7, calculate with scale of 8 then truncate > > This allows you to perhaps keep an input scale in the constructor and als= o > maintain consistency across various calculations. But whatever the behavi= or > is, it should be mathematically sound, consistent across different syntax > for the same calculation, and never reducing scale UNLESS it is told to d= o > so in the calculation step OR during the value retrieval. > > Jordan > I'm inexperienced when it comes to maths and the precision here, but I do have some experience when it comes to what the business I work for wants. I've implemented BCMath in a couple of places where this kind of precision is necessary, and I found that whenever I do divisions I prefer having at least 2 extra digits. Would it make sense to internally always just store a more accurate number? For things like additions/multiplications/subtractions it could always use the highest precision, and then for divisions add like +3~6 or something. Whenever you have numbers that have a fraction like `10.5001` it makes sense to set it to 4, but when you have `10` it suddenly becomes 0 when implicitly setting it. For the following examples assume each number is a BcNum: When doing something like `10 * 10.0000 * 10.000000000` I want the end result to have a precision of at least 9 so I don't lose information. When I do `((10 / 3) * 100) * 2` I don't want it to implicitly become 0, because the precision here is important to me. I don't think using infinite precision here is a reasonable approach either. I'm not sure what the correct answer is, perhaps it's just "always manually set the precision"? --00000000000059f23206151a5630 Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable


=
On Tue, Apr 2, 2024 at 11:17=E2=80=AF= AM Jordan LeDoux <jordan.ledo= ux@gmail.com> wrote:


On Sat, Mar 30, 2024 at 5= :09=E2=80=AFPM Saki Takamachi <saki@sakiot.com> wrote:
Hi Jordan,

Your opinion may be reasonable given the original BCMath calculation order.= That is, do you intend code like this?

Signature:
```
// public function __construct(string|int $number)
// public function getNumber(?int $scale =3D null): string
```

Add:
```
// public function add(Number|string|int $number): string

$num =3D new Number('1.23456');
$num2 =3D new Number('1.23');

$add =3D $num + $num2;
$add->getNumber(); // '2.46456'
$add->getNumber(1); // =E2=80=982.4'

$add =3D $num->add($num2);
$add->getNumber(); // '2.46456'
$add->getNumber(1); // '2.4'
```

Div:
```
// public function div(Number|string|int $number, int $scaleExpansionLimit = =3D 10): string


// case 1
$num =3D new Number('0.0001');
$num2 =3D new Number('3');

$div =3D $num / $num2; // scale expansion limit is always 10
$div->getNumber(); // '0.0000333333333'

$div =3D $num->div($num2, 20);
$div->getNumber(); // '0.00003333333333333333333'
$div->getNumber(7); // =E2=80=980.0000333'


// case 2
$num =3D new Number('1.111111');
$num2 =3D new Number('3');

$div =3D $num->div($num2, 3);
$div->getNumber(); // '0.370'
$div->getNumber(7); // =E2=80=980.3700000'
```

Since the scale can be inferred for everything other than div, a special ar= gument is given only for div.

Regards.

Saki

Something like the signature for `getN= umber()` in this example would be a decent solution. Operations which have = ambiguous scale (of which truly only div is in the BCMath library) should *= require* scale in the method that calls the calculation, however for consis= tency I can certainly see the argument for requiring it for all calculation= methods. The issue is how you want to handle that for operator overloads, = since you cannot provide arguments in that situation.

Probably the most sensible way (and I think the way I handled it a= s well in my library) is to look at both the left and right operand, grab t= he calculated scale of the input for both (or the set scale if the scale ha= s been manually set), and then calculate with a higher scale. If internally= it produces a rounded result, the calculation should be done at `$desireSc= ale + 2` to avoid compound rounding errors from the BCMath library and then= the implementation. If the result is truncated, the calculation should be = done at `$desiredScale + 1` to avoid calculating unnecessary digits.
<= div>
So we have multiple usage scenarios and the behavior nee= ds to remain consistent no matter which usage occurs, and what order the it= ems are called in, so long as the resulting calculation is the same.
<= div>
**Method Call**
$bcNum =3D new Number('1.0= 394567'); // Input scale is implicitly 7
$bcNum->div(&= #39;1.2534', 3); // Resulting scale is 3
$bcNum->div('= 1.2534'); // Implicit scale of denominator is 4, Implicit scale of nume= rator is 7, calculate with scale of 8 then truncate

**Operators**
$bcNum =3D new Number('1.0394567'); /= / Input scale is implicitly 7
$bcNum / '1.2534'; //=20 Implicit scale of denominator is 4, Implicit scale of numerator is 7, calcu= late with scale of 8 then truncate

This allow= s you to perhaps keep an input scale in the constructor and also maintain c= onsistency across various calculations. But whatever the behavior is, it sh= ould be mathematically sound, consistent across different syntax for the sa= me calculation, and never reducing scale UNLESS it is told to do so in the = calculation step OR during the value retrieval.

Jo= rdan

I'm inexperi= enced when it comes to maths and the precision here, but I do have some exp= erience when it comes to what the business I work for wants. I've imple= mented BCMath in a couple of places where this kind of precision is necessa= ry, and I found that whenever I do divisions I prefer having at least 2 ext= ra digits. Would it make sense to internally always just store a more accur= ate number? For things like additions/multiplications/subtractions it could= always use the highest precision, and then for divisions add like=C2=A0+3~= 6 or something. Whenever you have numbers that have a fraction like `10.500= 1` it makes sense to set it to 4, but when you have `10` it suddenly become= s 0 when implicitly setting it.=C2=A0

For the following examp= les assume each number is a BcNum:
When doing something like `10 = * 10.0000 * 10.000000000` I want the end result to have a precision of at l= east 9 so I don't lose information. When I do `((10 / 3) * 100) * 2` I = don't want it to implicitly become 0, because the precision here is imp= ortant to me. I don't think using infinite precision here is a reasonab= le approach either. I'm not sure what the correct answer is, perhaps it= 's just "always manually set the precision"?
--00000000000059f23206151a5630--