#### Do you have a question? Post it now! No Registration Necessary.  Now with pictures!

•  Subject
• Author
• Posted on

Hi guys, i have to ask you 10 seconds of your time ;)
Me and other from the it.comp.www.php group have found a strange
behavior of number_format() function, and we dont know if it depends
on the php version installed, math libs, or whatever.

Please, run this simple code and watch the output:
--------------------------
\$a = 0.715;
\$b = 0.615;
\$a_r_up = 0;
\$a_r_down = 0;
\$b_r_up = 0;
\$b_r_down = 0;
for(\$i = 0; \$i < 1000; \$i++){
\$ia = number_format(\$i + \$a, 2);
\$ib = number_format(\$i + \$b, 2);
list(\$a_int, \$a_dec) = explode('.', \$ia);
\$a_dec == 72 ? \$a_r_up++ : \$a_r_down++;
list(\$b_int, \$b_dec) = explode('.', \$ib);
\$b_dec == 62 ? \$b_r_up++ : \$b_r_down++;
}
echo \$a . ' rounded UP: ' . \$a_r_up . ', rounded DOWN: ' . \$a_r_down .
'<br />';
echo \$b . ' rounded UP: ' . \$b_r_up . ', rounded DOWN: ' . \$b_r_down;
--------------------------

In my case, for example, the output is:
0.715 rounded UP: 929, rounded DOWN: 71
0.615 rounded UP: 978, rounded DOWN: 22

How is possible that for some integer, number_format round the decimal
part (0.x15), and for some other it round it down?
For 0.715, it round down just when the integer part is between 256 and
326 (in my case).

Tested on: Win2003 IIS6, Linux Debian apache2, Linux Ubuntu 9.04
apache2, in all cases is php5.

<snip example>

This is the nature of floating point numbers. Many decimal fractions cannot
be represented exactly in a binary floating point number, just as 1/3 cannot
be represented exactly in base 10, although it *can* be represented exactly
in base three (1/3 = 0.1, 2/3 = 0.2, 3/3 = 1.0). This is not limited to any
particular "version of PHP" but is a feature of the hardware and affects
every single floating point number in any language on any machine.

Irrelevant, see above.

rf wrote:

Not quite true.  For instance, COBOL on IBM mainframes can use packed
decimal, which does represent decimal numbers exactly (hardware
supported).  And the bcd library in PHP can also represent it exactly
(software supported).

--
==================
Remove the "x" from my email address
Jerry Stuckle
JDS Computer Training Corp.
jstucklex@attglobal.net
==================

Correct. But we are talking about floating point, not packed decimal. That's
why I said "every single *floating point* number in any language on any
machine".

And floating point on an IBM mainframe also exhibits the problem.

But the problem here is not the float operation, but the number_format
() output.
Dumping the addittion, will result as the exact floating value (X.
715);
After number_format, comes the trouble (X.7149999999999999999999)..

I solved this writing my own number_format function.

I dont need the bc library in this case becose i wont use more than 4
decimals

Next time you post here please quote a bit of the previous conversation so
we can see exactly what you are answering. And in this case you are actually
answering the wrong post,  a not uncommon problem for someone using the

Are you sure of this? I am not.

I suggested? The operative thing is that floating point numbers are
normalised. It does not matter that you think you have something before the
"decimal point" and something after it. When your number is converted to
floating point and stuffed into a floating point register things change
dramatically. Floating point numbers are *not* exact, well mostly.

What bc library?

In any case this is most definately not a PHP issue.

That's the point.

Wroted in 5 mins, still need work:
function my_number_format(\$num, \$dec = 2, \$dec_sep = ',', \$thu_dec =
'.'){
if(strstr(\$num, '.')){
list(\$intero, \$decimali) = explode('.', (string)\$num);
}else{
\$intero = \$num;
\$decimali = 0;
}
\$nDec = strlen(\$decimali);
\$decCut = substr(\$decimali, 0, \$dec);
if(\$nDec > \$dec){
\$lastDec = \$decimali[\$dec];
if(\$lastDec >=3D 5){
\$decCut++;
}
}
if(strlen(\$decCut) > \$dec){
\$intero++;
\$decCut = '';
for(\$i=3D0; \$i !=3D \$dec; \$i++){
\$decCut .=3D '0';
}
}
while(strlen(\$decCut) < \$dec){
\$decCut = \$decCut . '0';
}
\$interoS = number_format(\$intero, 0, '', \$thu_dec);
if(\$dec =3D 0){
return \$interoS;
}else{
return \$interoS . \$dec_sep . \$decCut;
}
}

Simply, i treat the decimals as integers, cut the number of digits
after the zero, if the last digit usefull is >=3D 5 increment it, else
not.
Then 'rebuild together' the number, as a float.

http://uk.php.net/manual/en/ref.bc.php

Who cares if the problem is about php itself or the hardware running
it, or whatever?
I have that:

270 + 0.715 = 270.71 <-
and
215 + 0.715 = 215.72 <-

And need to solve this situation ;)

Binary floating point cannot represent X.715 with less than an
infinite number of bits.  (If a number has two or more digits after
the decimal point, there's no exact representation of it in binary
floating point unless those digits are 25 or 75, disregarding
trailing zeroes.)

If you're getting it printed as X.715, you're not printing it with
enough digits.

*Decimal* floating point does not have this problem, but few machines
have decimal floating point hardware.

2.715 as long double:
Before:
2.71499999999999999970509700908394279394997283816337585449218750000000000000000000
Value:
2.71499999999999999992193744358104368075146339833736419677734375000000000000000000
After:
2.71500000000000000013877787807814456755295395851135253906250000000000000000000000

2.715 as double:
Before:        2.714999999999999413802242997917346656322479248046875000000000
Value:         2.714999999999999857891452847979962825775146484375000000000000
After:         2.715000000000000301980662698042578995227813720703125000000000

2.715 as float:
Before:        2.714999675750732421875000000000000000000000000000000000000000
Value:         2.714999914169311523437500000000000000000000000000000000000000
After:         2.715000152587890625000000000000000000000000000000000000000000

This is the nature of *binary* floating point numbers.  Decimal
floating point (yes, there is such a thing) doesn't have this
problem.  However, very few machines (especially the ones in common
use) have decimal floating point hardware.  Decimal floating point
software is a little bit more common.

Nitpick:  *binary* floating point number.