Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:122902 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 8325A1A009C for ; Tue, 2 Apr 2024 23:51:03 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1712101891; bh=W4MEnMuQB0je+/eFNCab9dXRQ4w5Twy2gxk2xDmdpdA=; h=Subject:From:In-Reply-To:Date:Cc:References:To:From; b=RR1voO8vbhUB+Fbi/Zn78+Y54pTOqrp2jO+dMmmwaXHHSLcImGdmSqm4ToT5I7gos dWEgHnHpGqV6wTjzLkT00GV9ZqKG7GGDIFPR0Pof2Nrlw1n6ALnosIth3+LmuckXra Fvh5gjXFyAznbc6guTp5QKmDbPJvqFyxS8lmwclCAhPUAXMKC+Ly2FCoeEiKQ75DRM HxxFiJrOSkEd45ocKl7Bnx+USsT6BeRvPKCDsZZXJxtF+aB2wYKkN+VVDT24EcZP3W GtnkjzLOcRyT+RrGU2LI9FgtNQKwoYxmDiAU+3mLRhPQuZ28DiUYhp3jFruRNE12CS dN7ogn0MPriJg== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 8CE5718066F for ; Tue, 2 Apr 2024 23:51:28 +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,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.sakiot.com (mail.sakiot.com [160.16.227.216]) (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 23:51:27 +0000 (UTC) Received: from smtpclient.apple (unknown [117.55.37.250]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.sakiot.com (Postfix) with ESMTPSA id A9DED401D9; Wed, 3 Apr 2024 08:50:56 +0900 (JST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=sakiot.com; s=default; t=1712101856; bh=W4MEnMuQB0je+/eFNCab9dXRQ4w5Twy2gxk2xDmdpdA=; h=Subject:From:In-Reply-To:Date:Cc:References:To:From; b=fYM7TN2JIFFgjgDEqh1vGner1Q48tYa/PyZO6k9YvxCwHXAkWXr+242nqQYuJKaUr Ga/aW1ki1jdhbxxipM4NlUxKCn5darlnpFWp9597GTEZp20a9hT64t3tZf419YyNXi Q3lO1BYWsh86yO1UzQNzaG88N10aWCW5tYid2wkQ= Content-Type: text/plain; charset=us-ascii Precedence: bulk list-help: list-post: List-Id: internals.lists.php.net Mime-Version: 1.0 (Mac OS X Mail 16.0 \(3731.700.6\)) Subject: Re: [PHP-DEV] [RFC] [Discussion] Support object type in BCMath In-Reply-To: Date: Wed, 3 Apr 2024 08:50:43 +0900 Cc: Lynn , Aleksander Machniak , php internals Content-Transfer-Encoding: quoted-printable Message-ID: <655FEA80-9AB4-4AAD-A310-70ED968C97A2@sakiot.com> References: <4F094EDA-5058-407D-AF39-06FD934FDE1F@sakiot.com> <68CF373E-6ABF-4471-8992-813B4BA1B508@sakiot.com> <904197f4-afb5-401e-9e17-7a655c5449d0@alec.pl> To: Jordan LeDoux X-Mailer: Apple Mail (2.3731.700.6) From: saki@sakiot.com (Saki Takamachi) Hi Barney, Jordan, > I think that's a sufficiently different data type that it should be a = different class (if required), and probably a separate RFC, and for now = it's better to stay closer to the existing BCMath API. >=20 > Developers should be prepared to accept that an arbitrary precision = decimal can't represent 1/3 exactly, just like a binary float can't = represent 1/10 exactly. > Again, my experience on the issue is with the development of my own = library on the issue, however in my case I fully separated that kind of = object into its own class `Fraction`, and gave the kinds of operations = we've been discussing to the class `Decimal`. Storing numerators and = denominators for as long as possible involves a completely different set = of math. For instance, you need an algorithm to determine the Greatest = Common Factor and the Least Common Multiple in such a class, because = there are a lot of places where you would need to find the smallest = common denominator or simplify the fraction. >=20 > Abstracting between the `Fraction` and `Decimal` so that they worked = with each other honestly introduced the most complex and inscrutable = code in my entire library, so unless fractions are themselves also a = design goal of this RFC, I would recommend against it. > Having two classes `Fraction` and `Decimal` necessitated that I had a = `Number` class they both extended, as there are many situations where I = would want to type-hint "anything that calculation can be done on with = arbitrary precision" instead of specifically one or the other. I also = provided the `NumberInterface`, `DecimalInterface`, and = `FractionInterface`, though I don't think that would be necessary here = as this is much more just a wrapper for BCMath than an extension of it. = The main goal of my library was not to act as a wrapper for BCMath, it = was to EXTEND BCMath with additional capabilities, such as trigonometric = functions that have arbitrary precision, so keep that in mind when = weighing input of mine that is referencing the work I have done on this = topic. The design goals were different. Agree. I was a little too focused on precision and lost sight of the = larger goal. > In my library, if the scale is unspecified, I actually set the scale = to 10 OR the length of the input string, including integer decimals, = whichever is larger. Since I was designing my own library I could do = things like that as convention, and a scale of 10 is extremely fast, = even with the horrifically slow BCMath library, but covers most use = cases (the overwhelmingly common of which is exact calculation of = money). >=20 > My library handles scale using the following design. It's not = necessarily correct here, as I was designing a PHP library instead of = something for core, AND my library does not have to deal with operator = overloads so I'm always working with method signatures instead, AND it's = possible that my class/method design is inferior to other alternatives, = however it went: >=20 > 1. Each number constructor allowed for an optional input scale. > 2. The input number was converted into the proper formatting from = allowed input types, and then the implicit scale is set to the total = number of digits. > 3. If the input scale was provided, the determined scale is set to = that value. > 4. Otherwise, the determined scale at construction is set to 10 or the = implicit scale of "number of digits", whichever is larger. > 5. The class contained the `roundToScale` method, which allowed you to = provide the desired scale and the rounding method, and then would set = the determined scale to that value after rounding. It contained the = `round` method with the same parameters to allow rounding to a specific = scale without also setting the internal determined scale at the same = time. > 6. The class contained the `setScale` method which set the value of = the internal determined scale value to an int without mutating the value = at all. > 7. All mathematical operation methods which depended on scale, (such = as div or pow), allowed an optional input scale that would be used for = calculation if present. If it was not present, the internal calculations = were done by taking the higher of the determined scale between the two = operands, and then adding 2, and then the result was done by rounding = using the default method of ROUND_HALF_EVEN if no rounding method was = provided. >=20 > Again, though I have spent a lot of design time on this issue for the = math library I developed, my library did not have to deal with the RFC = process for PHP or maintain consistency with the conventions of PHP = core, only with the conventions it set for itself. However, I can = provide a link to the library for reference on the issue if that would = be helpful for people that are contributing to the design aspects of = this RFC. The two use cases at issue here are when the div and pow's exponent are = negative values. So how about allowing only these two methods to = optionally set `$scale` and `$roundMode` ? - The constructor takes only `$num` and always uses implicit scaling. = There is no option for the user to specify an arbitrary scale. - `$scale`: If specified, use that value, otherwise use `10`. The scale = specified here is added to the scale of the left operand and used as the = scale of the result. In other words, `(new Number('0.01')->div('3', 2))` = results in `'0.0030' // scale =3D 2 + 2 =3D 4`. - `$roundMode`: Specifies the rounding method when the result does not = fit within the scale. The initial value is `PHP_ROUND_TOWARD_ZERO`, = which matches the behavior of the BCMath function. That is, just = truncate. - If lucky enough to get the result within the scale, apply the implicit = scale to the result. In other words, if calculate `1 / 2`, the resulting = scale will be `1`, even if scale is `null` or specify a value such as = `20` for scale. - The result of a calculation with operator overloading is the same as = if the option was not used when executing the method. However, I'm not sure if naming it `$scale` is appropriate. Also, since `BCMath\Number` is not made into a final class, there is a = possibility of implementing an inherited class in userland. Regarding = this, is it better to make the calculation method a final method, or to = use a function overridden by the user when executing from the opcode? Regards. Saki=