Re: [RFC] [Discussion] Support object type in BCMath

From: Date: Tue, 02 Apr 2024 11:26:26 +0000
Subject: Re: [RFC] [Discussion] Support object type in BCMath
References: 1  Groups: php.internals 
Request: Send a blank email to internals+get-122853@lists.php.net to get a copy of this message
Hi Jordan, Lynn, > Something like the signature for getNumber() 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 > consistency 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 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 = 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 = 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 also maintain consistency > across various calculations. But whatever the behavior is, it should be mathematically sound, > consistent across different syntax for the same calculation, and never reducing scale UNLESS it is > told to do so in the calculation step OR during the value retrieval. > 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"? Thanks for the important perspective feedback. One thing I overlooked: if the exponent of pow is negative, the scale of the result becomes unpredictable, just like with div. e.g. ``` 3 ** -1 = 0.333333..... ``` Also, an idea occurred to me while reading your comments. 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. This means that when we do division, no division is done internally, but we actually multiply the denominator. At the very end of the process, when converting to string, any reserved division is performed according to the specified scale. If we have the option of not specifying a scale when converting to a string, it may be preferable to convert based on an implicit scale. Regards. Saki

« previous php.internals (#122853) next »