Re: [PHP-DEV] Add bcdivmod() to BCMath

Thanks all,

My only question is the pseudo-tuple return, which is rarely used in PHP (although I've used it myself, I think exactly once), and I think this would be the first use of it in a built in library. (I may be wrong on that.) I don't have a particular alternative to suggest, just flagging that as the main part that could warrant discussion.

It's actually already used by gmp_div_qr(), so +1 from me.

I predicted this would probably be on the agenda. Another idea is to pass arguments by reference, like in `exec()`.

Personally, I find something like a tuple easier to use. However, without generics, all we know is that the return type is an array, which may be inconvenient in terms of IDE completion, etc.

I'm wondering whether this needs to be a change to the API, or if it might be better implemented as a purely internal optimisation to BCMath / Number - in some way memoize both the quotient and the remainder when either div or mod is called, and then document for users that calling one after the other will be extremely cheap to run.

But maybe that brings up too many questions about memory usage - e.g. is the memory for this reserved in advance for each instance of Number, or allocated when one of these functions is called, and should it be freed even while the object remains in scope to avoid using lots of memory if many numbers are divided or modded.

I am considering implementing this not only as a Number class, but also as a function.

Also, it is not practical to record the remainder in the Number object resulting from the division. This is because there can be objects whose division results are equal but whose remainders are different.

In the discussion of the Number class, it was argued that objects should not have too much state.

Increasing the state of surplus is not desirable in this respect, as it contradicts the previous argument.

Regards,

Saki

---- On Tue, 25 Jun 2024 17:29:58 +0100 Saki Takamachi wrote ---
>
> Also, it is not practical to record the remainder in the Number object resulting from the division. This is because there can be objects whose division results are equal but whose remainders are different.
>
> In the discussion of the Number class, it was argued that objects should not have too much state.
>
> Increasing the state of surplus is not desirable in this respect, as it contradicts the previous argument.

If anything I was thinking of recording the remainder in the the original number object, not the one returned (or possibly in something like a WeakMap keyed to it). So it does increase state and makes it technically no longer immutable, but that change in state would not be detectable from outside except by profiling performance. It's state that would have to be ignored in the equality check.

Hi Barney,

If anything I was thinking of recording the remainder in the the original number object, not the one returned (or possibly in something like a WeakMap keyed to it). So it does increase state and makes it technically no longer immutable, but that change in state would not be detectable from outside except by profiling performance. It's state that would have to be ignored in the equality check.

I see, I understand.

But I don't think that's wise. For example, if we reversed the order of the div and mod, there would be no cache and we wouldn't get the speed boost. (Or does it cache the quotient as well?)

Also, even if the order is as expected, if another division is performed in between, the cache will be overwritten and it will become meaningless.

The main concern is the cost of having to mod data during division for the surplus that we may never use.

There is the issue of data capacity, but the extra processing such as memory allocation will inevitably occur, so for those who don't need mod, the change will make slow down.

Regards,

Saki

Hey Benjamin,

I predicted this would probably be on the agenda. Another idea is to pass arguments by reference, like in exec().

Personally, I find something like a tuple easier to use. However, without generics, all we know is that the return type is an array, which may be inconvenient in terms of IDE completion, etc.

As for tuple vs reference, I think the general direction is to move away from references as much as possible, and AFAIK references actually make things harder for IDEs and static analysis tools, whereas the tuple syntax array{string, string} is well understood at least by PhpStorm, Psalm and PHPStan, which can correctly type the function’s return value in their stubs.

Full ack on this.

I’m wondering if an array vs an object allocation makes a difference here, or if the amount of bcdivmod() execution dwarfs this sort of concern?

Thinking:

$result = \bcdivmod('123', '2');

echo $result->quotient; // '61'
echo $result->remainder; // '1'

No idea if that’s relevant, so I’m throwing it in the room.

···

Marco Pivetta

https://mastodon.social/@ocramius

https://ocramius.github.io/

I see, I understand.

But I don’t think that’s wise. For example, if we reversed the order of the div and mod, there would be no cache and we wouldn’t get the speed boost. (Or does it cache the quotient as well?)

I don’t think the cache is a good idea either, for the reasons you mentioned.

I predicted this would probably be on the agenda. Another idea is to pass arguments by reference, like in exec().

Personally, I find something like a tuple easier to use. However, without generics, all we know is that the return type is an array, which may be inconvenient in terms of IDE completion, etc.

As for tuple vs reference, I think the general direction is to move away from references as much as possible, and AFAIK references actually make things harder for IDEs and static analysis tools, whereas the tuple syntax array{string, string} is well understood at least by PhpStorm, Psalm and PHPStan, which can correctly type the function’s return value in their stubs.

— Benjamin

Hi Marco, Benjamin,

As for tuple vs reference, I think the general direction is to move away from references as much as possible, and AFAIK references actually make things harder for IDEs and static analysis tools, whereas the tuple syntax array{string, string} is well understood at least by PhpStorm, Psalm and PHPStan, which can correctly type the function's return value in their stubs.

I see, I agree. This problem seems to be solved by using PHPDoc.

I'm wondering if an array vs an object allocation makes a difference here, or if the amount of `bcdivmod()` execution dwarfs this sort of concern?

Thinking:

$result = \bcdivmod('123', '2');

echo $result->quotient; // '61'
echo $result->remainder; // '1'

No idea if that's relevant, so I'm throwing it in the room.

BCMath is significantly faster in master, so its cost may have a significant impact.

For reference, here is a speed comparison with 8.3 on my env. The benchmark used is the code from ext-decimal, which is often introduced in the context of "BCMath is slow”. The unit of all measurement results is "seconds".
https://php-decimal.github.io/#performance

8.3
- add int: 3.7771
- add string: 3.0387
- sub int: 3.491
- sub string: 3.0248
- mul int: 5.3318
- mul string: 5.7315
- div int: 10.6659
- div string: 25.762

Master
- add int: 1.72
- add string: 1.4444
- sub int: 1.763
- sub string: 1.4745
- mul int: 2.2038
- mul string: 2.0621
- div int: 2.8515
- div string: 2.9411

Regards,

Saki