Question

Got another math calculation problem again.

$a = 34.56

$b = 34.55

$a do some calculation to get this figure

$b is doing rounding nearest 0.05 to get this figure

what happen is

$c = $b - $a

supposedly I should be -0.01, but I echo out the $c is show -0.00988888888888

I try to use number_format($c, 2), but the output is 0.00,

how can I make sure $a and $b is exactly 2 decimals, no hidden number at the back.

in my php knowledge number_format only able to format the display, but the value not really 2 decimal,

I hope I can get help from here. This really frustrated me.

Was it helpful?

Solution

Try sprintf("%.2f", $c);

Floating point numbers are represented in IEEE notation based on the powers of 2, so terminating decimal numbers may not be a terminating binary number, that's why you get the trailing digits.

As suggested by Variable Length Coder, if you know the precision you want and it doesn't change (e.g. when you're dealing with money) it might be better to just use fixed point numbers i.e. express the numbers as cents rather than dollars

$a = 3456;

$b = 3455;

$c = $b - $a;

sprintf ("%.2f", $c/100.0);

This way, you won't have any rounding errors if you do a lot of calculations before printing.

OTHER TIPS

Use round():

$c = round($b - $a, 2);

Note: you can also choose the rounding mode as appropriate.

Edit: Ok I don't get what sprintf() is doing that number_format() isn't:

$c = number_format($b - $a, 2);

vs

$c = sprintf("%.2f", $b - $a);

?

Native Calculation:

$a = 34.56;
$b = 34.55;
$c = $b - $a; // -0.010000000000005    

Works as expected (! use always BC functions for real number calculations, the issue is for all C based platforms):

$a = '34.56';
$b = '34.55';
$c = bcsub($b, $a, 4); // -0.0100    

I also ran into this issue recently when doing calculations with floats. For example, I had 2 floats that when subtracted and formatted, the value was -0.00.

$floatOne = 267.58;
$floatTwo = 267.58;
$result = number_format($floatOne - floatTwo, 2);
print $result; //printed a string -0.00

What I did was:

$result = abs($floatOne - $floatTwo);// Made the number positive
print money_format('%i', $result); // printed the desired precision 0.00

In my solution I know that floatOne will never be less than floatTwo. The money_format function is only defined if the system has strfmon capabilities, Windows does not.

You've run into one of the traps of floating point numbers; that they cannot always represent exact decimal fractions. If you want exact decimal values, you're better off using integers and then dividing by the precision you want at the end.

For example, if you're doing calculations in floats represeting Dollars (or your favorite currency), you may want to actually do your calculations in integer cents.

You can very neatly sidestep all of these issues simply by using the bcmath library.

Just remember to read the documentation and be careful whether you are passing arguments as strings or as numeric datatypes.

If still somebody reach this page with similar problems where floating number subtraction causes error or strange values. I want to explain this problem with a bit more details. The culprit is the floating point numbers. And different operating systems and different versions of programming languages can behave differently.

To illustrate the problem, I will explain why with a simple example below.

It is not directly related to PHP and it is not a bug. However, every programmer should be aware of this issue.

This problem even took many lives two decades ago.

On 25 February 1991 this problem in floating number calculation in a MIM-104 Patriot missile battery prevented it intercepting an incoming Scud missile in Dhahran, Saudi Arabia, contributing to the death of 28 soldiers from the U.S. Army's 14th Quartermaster Detachment.

But why it happens?

The reason is that floating point values represent a limited precision. So, a value might not have the same string representation after any processing. It also includes writing a floating point value in your script and directly printing it without any mathematical operations.

Just a simple example:

$a = '36';
$b = '-35.99';
echo ($a + $b);

You would expect it to print 0.01, right? But it will print a very strange answer like 0.009999999999998

Like other numbers, floating point numbers double or float is stored in memory as a string of 0's and 1's. How floating point differs from integer is in how we interpret the 0's and 1's when we want to look at them. There are many standards how they are stored.

Floating-point numbers are typically packed into a computer datum as the sign bit, the exponent field, and the significand or mantissa, from left to right....

Decimal numbers are not well represented in binary due to lack of enough space. So, uou can't express 1/3 exactly as it's 0.3333333..., right? Why we can't represent 0.01 as a binary float number is for the same reason. 1/100 is 0.00000010100011110101110000..... with a repeating 10100011110101110000.

If 0.01 is kept in simplified and system-truncated form of 01000111101011100001010 in binary,when it is translated back to decimal, it would be read like 0.0099999.... depending on system (64bit computers will give you much better precision than 32-bits). Operating system decides in this case whether to print it as it sees or how to make it in more human-readable way. So, it is machine-dependent how they want to represent it. But it can be protected in language level with different methods.

If you format the result, echo number_format(0.009999999999998, 2); it will print 0.01.

It is because in this case you instruct how it should be read and how precision you require.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top