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"?

Reply via email to