On Tue, Apr 2, 2024 at 10:24 AM Jordan LeDoux <jordan.led...@gmail.com>
wrote:

>
>
> On Tue, Apr 2, 2024 at 3:12 AM Lynn <kja...@gmail.com> 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 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"?
>>
>
> 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
>

An addendum:

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.

Jordan

Reply via email to