I think I tried all the examples from the net! This is my PHP code:
echo(number_format(22.95 * 4.1, 2));
The result is 94.10 with PHP. I have been trying to get the same result with JavaScript, but no matter what I try, its always 94.09. Any idea how to make that work?
Some of the code I tried:
var x = 22.95;
var y = 4.10;
var res = x * y;
var res1 = res.toFixed(3);
console.log(res);
console.log(res1);
console.log(Number(res1).toFixed(2));
console.log(Math.round(res * 100) / 100);
Thanks!
2
Answers
You experiencing a floating point error on the JavaScript side.
22.95*4.1
is not94.095
(as you might expect) but94.09499999999998
in (32bit) floating-point math, therefor rounded down to.09
and not up to.10
.There’s no good way to fix this in JavaScript but you can force PHP to behave like JavaScript using
ini_set('precision', 17)
.See answer from question Why can PHP calculate 0.1 + 0.2 when other languages fail?:
Albeit both, PHPs’
number_format()
as well as JSs’Number.prototype.toFixed()
do rounding, the rounding is different between those two functions.In number_format() PHP rounds up if the sub-fraction is .5 or higher, and down if lower.
To get the same result in JS with a precision of two digits:
To get the same result in PHP with a precision of two digits:
Setting the precision like ini_set(‘precision’, 17); as suggested in a different answer by Niklas E., changed nothing to that for me. It was likely not too little for the numbers in the example that it could have had any effect.
While there is floating point precision and it can be unexpected (floating point rounding errors, cf. What causes floating point rounding errors?), both PHP and JS are using the same standard for floating point numbers:
PHP
float
"typically uses the IEEE 754 double precision format." (ref)JS "
Number
type is a double-precision 64-bit binary format IEEE 754 value" (ref)So the difference here can be explained by the rounding only. PHPs’
number_format()
rounds "half up" while in JS it is also that way, however it looks like that.toFixed()
only rounds "if necessary", whatever that means, perhaps only for the last digit.This makes not much sense for formatting numbers to users, e.g. many use-cases even when displaying only two digits, the internal rounding is for four digits (book keeping).
So in the end this may show as a precision error, which may fall into the context of the other answer, and specifically for that
.toFixed(n)
has too little precision when rounding the internal value (or perhaps when deciding about the rounding, the description on MDN has something like "rounding when necessary").Here the
.toFixed(3/4)
rounds when necessary from four/five digits of the internal representation andMath.round()
then creates a new number that is not prone any longer to the floating point precision errors of.toFixed()
. TheMath.round()
results in a new number that is rounded for the precision of two digits, therefore divided by one hundred (ten times ten, two digits).Then
.toFixed(2)
can’t ruin that.A similar rounding can be done by first rounding for three/four digits, and then for two digits:
Using the
.toFixed(2)
function then will give the expected string:Applying
toFixed(1)
first (comment by evolutionxbox) will make you loose precision you want to have in the format for display (much likely as you formatted the number with two digits).Wikipedia: Double-precision floating-point format