Float precision errors using number_format()
GusGold opened this issue · 3 comments
When using number_format() like here, you are relying on php's float precision.
Trying to use a number like Decimal::fromFloat(90.05)
will cause a precision error as number_format(90.05, 16, ".", "") === "90.0499999999999972"
.
There needs to be another method used to load in the numbers initially, as all following operation are going to be wrong.
Casting to string works as long as the number isn't too big or the fraction too small:
var_dump((string)90.05); // string(5) "90.05"
Unfortunately, it's pretty easy to get into scientific notation:
var_dump((string)9012908310238012.11) // string(18) "9.012908310238E+15"
var_dump((string)0.000021) // string(6) "2.1E-5"
It could work to check for "E+" or "E-" in the string and fall back to the number_format
method:
$strValue = (string)$fltValue;
if (preg_match("/E[\+\-]\d+$/", $strValue)) {
$strValue = number_format($fltValue, $scale, '.', '');
}
INF
and NAN
are already covered elsewhere in fromFloat()
so I have not considered them here.
Here's the 3v4l with my experiment: http://3v4l.org/0aVdK
@GusGold you can use something like Decimal::fromString("90.05");
.
fromFloat
exists because sometimes we have to start with native float numbers. By the way, the precision error isn't because the conversion, but because a native float number can't exactly represent the 90.05 number, so it isn't php-bignumbers fault. (ok, if $scale
is small we can have precision loss, but that's not the real problem).
In addition, the explanation of @shabbyrobe also holds.
EDIT: I've seen that @shabbyrobe proposes a solution to better cast "easy numbers" (not too big, not too small) , maybe it's possible to do a simple cast to string and call fromString. This evening I'll play with this to see if we can obtain better results.