Hi internals,
BCMath currently only has procedural functions. This is a bit unwieldy for some design patterns and I came up with the idea of supporting object types like mysqli.
Yet another idea is to also support immutable objects, like DateTime.
e.g.
$num1 = new BcNum('1.235');
$num1imm = new BcNumImmutable('1.235');
$num2 = new BcNum('2.001');
$num1result = $num1->add($num2);
$num1immResult = $num1imm->add($num2);
$num1->getValue(); // '3.236'
$num1result->getValue(); // '3.236'
$num1imm->getValue(); // '1.235'
$num1immResult->getValue(); // '3.236'
The reason why the class name is not "BCNum" is because it needs to follow the current PHP naming convention. As an example, the class name of a PDO subclass has a name such as "PdoOdbc".
I look forward to your feedback.
Regards.
Saki
Hi,
After thinking about it, this could be done in userland. I'll try creating a library myself first.
Regards.
Saki
Hi,
After thinking about it, this could be done in userland. I'll try creating a library myself first.
Regards.
Saki
Hi Saki
One advantage of a new API is that it could be more efficient than the current API.
The current API takes input as strings and outputs strings as a result, and therefore always has to reconstruct the BCMath internal state.
By keeping everything inside an object you could keep the internal state, improving performance.
Whether it makes a difference in practice, I don't know.
Kind regards
Niels
Hi Niels,
One advantage of a new API is that it could be more efficient than the current API.
The current API takes input as strings and outputs strings as a result, and therefore always has to reconstruct the BCMath internal state.
By keeping everything inside an object you could keep the internal state, improving performance.
Thanks for your feedback. I overlooked the cost of rebuilding the internal state. I'll give it a try and see if there's a significant difference in performance.
I also noticed earlier that it may be better in some convenience aspects, such as the way to compare values.
e.g.
$bcnum1 > $bcnum2 // bool
First, I'll try creating a simple prototype.
Regards.
Saki
Yet another idea is to also support immutable objects, like DateTime.
e.g.
$num1 = new BcNum('1.235'); $num1imm = new BcNumImmutable('1.235'); $num2 = new BcNum('2.001'); $num1result = $num1->add($num2); $num1immResult = $num1imm->add($num2);
I would suggest if you do this only supporting immutable objects, or at
least making immutable the default, the one with with the simpler name,
and reserving the mutable version specifically for when people want to
use mutability. The strings used with bcmath today are effectively
immutable.
It's hard to see why a number should be mutable though. Users of the
class can always wrap an immutable up it in a mutable object if they want.
If you do have both mutable and immutable it might be worth giving the
methods separate names to make the distinction clearer - e.g. "add" for
mutable, "plus" for immutable, and maybe making the add method return void.
Hi Barney,
I would suggest if you do this only supporting immutable objects, or at least making immutable the default, the one with with the simpler name, and reserving the mutable version specifically for when people want to use mutability. The strings used with bcmath today are effectively immutable.
It's hard to see why a number should be mutable though. Users of the class can always wrap an immutable up it in a mutable object if they want.
If you do have both mutable and immutable it might be worth giving the methods separate names to make the distinction clearer - e.g. "add" for mutable, "plus" for immutable, and maybe making the add method return void.
Thanks, that's what I was starting to worry about too. It seems like a good idea to support only immutability, as you say earlier in your proposal.
Regards.
Saki
Hi Barney,
Thanks, that's what I was starting to worry about too. It seems like a
good idea to support only immutability, as you say earlier in your proposal.Regards.
Saki
Using a BCNum inside a loop is the use case, where every loop would result
in memory allocation for a new object, as well as the overhead of the
constructor, etc.
Granted, only people who REALLY know what they are doing should be doing
this. Though my library which essentially IS a wrapped for BCMath that is
upgradeable if you install other extensions (like ext-decimal) does support
both, I suggest using primarily immutables in my docs.
That said, the C library itself for BCMath is insanely inefficient as far
as arbitrary precision math goes, so I would suggest that people don't get
their hopes up too much on the performance front.
Jordan
Hi Jordan,
Using a BCNum inside a loop is the use case, where every loop would result in memory allocation for a new object, as well as the overhead of the constructor, etc.
Granted, only people who REALLY know what they are doing should be doing this. Though my library which essentially IS a wrapped for BCMath that is upgradeable if you install other extensions (like ext-decimal) does support both, I suggest using primarily immutables in my docs.
That said, the C library itself for BCMath is insanely inefficient as far as arbitrary precision math goes, so I would suggest that people don't get their hopes up too much on the performance front.
I just sent an email, and you're right about performance. Therefore, the point of this proposal seems to be simply to improve convenience.
Regards.
Saki
Hi Jordan,
Using a BCNum inside a loop is the use case, where every loop would
result in memory allocation for a new object, as well as the overhead of
the constructor, etc.Granted, only people who REALLY know what they are doing should be doing
this. Though my library which essentially IS a wrapped for BCMath that is
upgradeable if you install other extensions (like ext-decimal) does support
both, I suggest using primarily immutables in my docs.That said, the C library itself for BCMath is insanely inefficient as
far as arbitrary precision math goes, so I would suggest that people don't
get their hopes up too much on the performance front.I just sent an email, and you're right about performance. Therefore, the
point of this proposal seems to be simply to improve convenience.Regards.
Saki
I've done a lot of performance tuning on my arbitrary precision library,
and will simply state for everyone here that I think the amount of
development effort involved in improving performance of the BCMath library
is almost certainly going to see a return on your effort that is not worth
it. There have been discussions over the last year on possibly working on
bundling a new arbitrary precision C library and providing something that
is performant enough to be generally useful in core, but that's not even at
the RFC stage, just investigations.
I wouldn't say that improving BCMath is a waste of time, but there is also
probably lower hanging fruit once you get past things like Saki has here.
DevEx improvements.
Jordan
Hi Jordan,
I've done a lot of performance tuning on my arbitrary precision library, and will simply state for everyone here that I think the amount of development effort involved in improving performance of the BCMath library is almost certainly going to see a return on your effort that is not worth it. There have been discussions over the last year on possibly working on bundling a new arbitrary precision C library and providing something that is performant enough to be generally useful in core, but that's not even at the RFC stage, just investigations.
I wouldn't say that improving BCMath is a waste of time, but there is also probably lower hanging fruit once you get past things like Saki has here. DevEx improvements.
BCMath is slow, but currently no other extension can replace it in terms of computational accuracy. Therefore, I would like to advance these proposals with the aim of improving usability rather than improving performance.
Since we have already completed the implementation of the important processes when creating the prototype, we will start preparing the RFC.
Regards.
Saki
Hi,
I created a prototype, although it's pretty rough. It can overload operators and calculate not only between BcNum
but also between BcNum
and int
or string
.
https://github.com/php/php-src/pull/13741
I compared the execution times.
Test code:
<?php
$start = microtime(true);
$num = new BcNum('3.650000001', 10);
$num2 = new BcNum('4.2335185', 10);
for ($i = 0; $i < 1000000; $i++) {
$num3 = str_repeat('1', rand(1, 1));
$num4 = str_repeat('1', rand(1, 1));
$num = $num + $num3;
$num2 = $num2 - $num4;
$num = $num + $num2;
}
var_dump('BcNum and string: ' . microtime(true) - $start);
$start = microtime(true);
$num = new BcNum('3.650000001', 10);
$num2 = new BcNum('4.2335185', 10);
for ($i = 0; $i < 1000000; $i++) {
$num3 = new BcNum(str_repeat('1', rand(1, 1)), 10);
$num4 = new BcNum(str_repeat('1', rand(1, 1)), 10);
$num = $num + $num3;
$num2 = $num2 - $num4;
$num = $num + $num2;
}
var_dump('BcNum and BcNum: ' . microtime(true) - $start);
$start = microtime(true);
$num = '3.650000001';
$num2 = '4.2335185';
for ($i = 0; $i < 1000000; $i++) {
$num3 = str_repeat('1', rand(1, 1));
$num4 = str_repeat('1', rand(1, 1));
$num = bcadd($num, $num3, 10);
$num2 = bcsub($num2, $num4, 10);
$num = bcadd($num, $num2, 10);
}
var_dump('bcadd, bcsub: ' . microtime(true) - $start);
Result:
string(33) "BcNum and string: 3.2775790691376"
string(32) "BcNum and BcNum: 4.3857049942017"
string(29) "bcadd, bcsub: 4.2680249214172"
The execution speed ranking does not change even if the calculation is done simply by adding one time. When calculating between BcNum
, the cost of creating an instance is probably high. However, since this difference was obtained after 1 million trials, it may be safe to assume that the difference is not that big.
Personally, I think the main point of this proposal is the increased convenience through operator overloading, not execution time.
Regards.
Saki