Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:122881 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 0D6D31A009C for ; Tue, 2 Apr 2024 17:25:00 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1712078727; bh=5rdZUCFDmQGt9nNByudHym/SZ96sA9xPmG6VLns2tEA=; h=References:In-Reply-To:From:Date:Subject:To:Cc:From; b=POXMqM27EYHoSh4XNAJ1A7QTzeCcjufBk+icVZaaDcII3HBtJqu7501aa/H9krIoE SnKOLwHfgPvUSITkUXNIOFAe8FSxelkr9f5uaJysqrkWH/MbGBeU3ck+hCpm/T4zKj xyFTf26J+KpTcsM2A5VwqPLA/E/7sJhUdfYlq9wrTzbiiYuGHigMBAtfhaHnRfduZW W+jtq3PHHRrUHnZzqs67jAaE7p2V0D6GIVFDjznX8RCE51946P4CA0IzIdEIQlm6DB eMFaK1rKusM+F1IoXYQPVREb+/E9E1dQvh20IYxuKlqI9zojmxP/OxXYu/kge9eDvJ DcjvWrJCqwALQ== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id E0106180083 for ; Tue, 2 Apr 2024 17:25:26 +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_H3,RCVD_IN_MSPIKE_WL, 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-pj1-f53.google.com (mail-pj1-f53.google.com [209.85.216.53]) (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 17:25:26 +0000 (UTC) Received: by mail-pj1-f53.google.com with SMTP id 98e67ed59e1d1-2a274955aacso458294a91.2 for ; Tue, 02 Apr 2024 10:24:58 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1712078697; x=1712683497; 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=5rdZUCFDmQGt9nNByudHym/SZ96sA9xPmG6VLns2tEA=; b=N+h4mvLjNIp0qj8GGn48G2wwQwjBMac41HIf9Z2TpQgslTMZVKXQH6/M0oRMxZRdfe qgQsetOQLYgNKUMd0sgmsW1/XheUxH05aYrokQDENOdPRWXPoI4uAsu7xaaXPaGL/94q pNeaTe6IFDrnCYcKAekxKCvZJxsBjtfGYnGg+SWDiTCZvrUQTaRid2wlwAOWOtL70QwX TA5rgIEmaGs3aRRoyhzWUA2cCkZutqN//cZjJ+zPpNd1kSu+1ec90kbHOTy/2VgNHO35 t3U3NvaMRmgQVPvuZrw/7uGG98oCU6oGqejMEQ7Q0TvtVCEeTHKmuZQbDzryL/A64Fd5 kVFg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1712078697; x=1712683497; 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=5rdZUCFDmQGt9nNByudHym/SZ96sA9xPmG6VLns2tEA=; b=VLVXXK5t8HORoUkXYfq93r+hYwlUnxG5KaYlAKmTYw7wsbg8g4RQQSzEoqErXNJpbb lDFCk3/gwQoXvPsNtkHmU1/cT4leGqeZ39cLXbaSMveOdSuoxs/uq8d6gCUyBTHihEpc vhEyEzXSAPWkeQEKwjRWUlnTEnrPzi/CtUZaik2BAcVlVxe5IeIKFnsI8AvZPSmVG0XR 5kipZ25p3HeW7oRnJLexjsP3+rKGx/STfsqT7YIAcGfd65xdI5Fyu1PawaFbz61JJvUS 2tlOKC7n8nsS9rUxads0e7290RfRR4P9Sm5bsJ7E3ZmwsiVK9OU1sAl7xmejRM66xKZb ov8A== X-Forwarded-Encrypted: i=1; AJvYcCXsEL6LV98PFxA2lonJXCsixNUVdewUefSgXUlQ4l8GuRqLlhqoCyQEEZQdVhcp23tTnsYJfyqa1gb7ZhVnT3MF8390Qp719w== X-Gm-Message-State: AOJu0YxjnMTCOVgMqdMS/q8moQQhcEoAnIZv6E8EQWvn6T+lOrRaZGnt uMiRIpDdgGdKU2SEQkAWMBDAPy9YjcIGK5oOY3yA4EQx+p8+KjpVdWIXUhzkQAiUiQ43M+FtxzS zls2ob8Tk7nEyFd+FlY+o8gMx5LBm/otc6dA= X-Google-Smtp-Source: AGHT+IFU1mGSfH3+1oDA4iBg0a4iDg2lg5L+R7cguH/2+bwqkJ/Yb6BVf0jsfXgACgbLwl+6MjJgpwQPftLLMbDVZDA= X-Received: by 2002:a17:90b:1c0b:b0:2a2:70c6:970c with SMTP id oc11-20020a17090b1c0b00b002a270c6970cmr1510465pjb.11.1712078697133; Tue, 02 Apr 2024 10:24:57 -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 10:24:42 -0700 Message-ID: Subject: Re: [PHP-DEV] [RFC] [Discussion] Support object type in BCMath To: Lynn Cc: Saki Takamachi , Aleksander Machniak , php internals Content-Type: multipart/alternative; boundary="00000000000091fe2d0615206254" From: jordan.ledoux@gmail.com (Jordan LeDoux) --00000000000091fe2d0615206254 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable On Tue, Apr 2, 2024 at 3:12=E2=80=AFAM Lynn wrote: > > 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 precisio= n > 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 yo= u > 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 settin= g > 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. Whe= n > I do `((10 / 3) * 100) * 2` I don't want it to implicitly become 0, becau= se > 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"? > 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). 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: 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. 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 current assumption is that a Number always holds a single value. How if we made it so that it held two values? They are the numerator and the denominator. 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. 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. Jordan --00000000000091fe2d0615206254 Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable


=
On Tue, Apr 2, 2024 at 3:12=E2=80=AFA= M Lynn <kjarli@gmail.com> wro= te:

I'm inexperienced when it come= s to maths and the precision here, but I do have some experience when it co= mes 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 th= at whenever I do divisions I prefer having at least 2 extra digits. Would i= t make sense to internally always just store a more accurate number? For th= ings like additions/multiplications/subtractions it could always use the hi= ghest precision, and then for divisions add like=C2=A0+3~6 or something. Wh= enever 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 implicitl= y setting it.=C2=A0

For the following examples assume each nu= mber is a BcNum:
When doing something like `10 * 10.0000 * 10.000= 000000` I want the end result to have a precision of at least 9 so I don= 9;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 do= n'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"?

In my library, if the scale is unspecified, I actually set = the scale to 10 OR the length of the input string, including integer decima= ls, whichever is larger. Since I was designing my own library I could do th= ings like that as convention, and a scale of 10 is extremely fast, even wit= h the horrifically slow BCMath library, but covers most use cases (the over= whelmingly common of which is exact calculation of money).

My library handles scale using the following design. It's not = necessarily correct here, as I was designing a PHP library instead of somet= hing 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 pos= sible that my class/method design is inferior to other alternatives, howeve= r it went:

1. Each number constructor allowed for = an optional input scale.
2. The input number was converted into t= he 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 p= rovided, 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 desir= ed scale and the rounding method, and then would set the determined scale t= o that value after rounding. It contained the `round` method with the same = parameters to allow rounding to a specific scale without also setting the i= nternal 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 mathem= atical operation methods which depended on scale, (such as div or pow), all= owed 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 h= igher 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.

Agai= n, though I have spent a lot of design time on this issue for the math libr= ary I developed, my library did not have to deal with the RFC process for P= HP 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 co= ntributing to the design aspects of this RFC.

>= =20 The current assumption is that a Number always holds a single value. How if we made it so that it held two values? They are the numerator and=20 the denominator.

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 `De= cimal`. Storing numerators and denominators for as long as possible involve= s a completely different set of math. For instance, you need an algorithm t= o determine the Greatest Common Factor and the Least Common Multiple in suc= h a class, because there are a lot of places where you would need to find t= he smallest common denominator or simplify the fraction.

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.

Jo= rdan
--00000000000091fe2d0615206254--