On Tue, Apr 2, 2024 at 11:17 AM Jordan LeDoux <jordan.led...@gmail.com> wrote:
> > > On Sat, Mar 30, 2024 at 5:09 PM Saki Takamachi <s...@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 = null): string >> ``` >> >> Add: >> ``` >> // public function add(Number|string|int $number): string >> >> $num = new Number('1.23456'); >> $num2 = new Number('1.23'); >> >> $add = $num + $num2; >> $add->getNumber(); // '2.46456' >> $add->getNumber(1); // ‘2.4' >> >> $add = $num->add($num2); >> $add->getNumber(); // '2.46456' >> $add->getNumber(1); // '2.4' >> ``` >> >> Div: >> ``` >> // public function div(Number|string|int $number, int >> $scaleExpansionLimit = 10): string >> >> >> // case 1 >> $num = new Number('0.0001'); >> $num2 = new Number('3'); >> >> $div = $num / $num2; // scale expansion limit is always 10 >> $div->getNumber(); // '0.0000333333333' >> >> $div = $num->div($num2, 20); >> $div->getNumber(); // '0.00003333333333333333333' >> $div->getNumber(7); // ‘0.0000333' >> >> >> // case 2 >> $num = new Number('1.111111'); >> $num2 = new Number('3'); >> >> $div = $num->div($num2, 3); >> $div->getNumber(); // '0.370' >> $div->getNumber(7); // ‘0.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 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. > > 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"?