[php-maint] Bug#603292: libapache2-mod-php5: PHP5 does not round correctly

Bob Proulx bob at proulx.com
Mon Nov 15 05:13:38 UTC 2010


Markus Knaup wrote:
> PHP5 does not round correctly.
> echo round(1.025, 2) . "<br />";

Note that this is the same behavior as C's printf.  Also the shell's
printf which makes that behavior available to the command line
produces the same behavior.

  $ printf "%.2f\n" 1.025
  1.02

But it just didn't seem satisfying to say that it isn't a bug in PHP
because that is just the way floating point computer engines work.
But that is basically the reason.  That is the way floating point
works on machines.  But being unsatisfying I wanted to add some more
information about the problem.

For the underlying reasons you need to look at the encoded binary
values.  A typical issue is that exact terminating decimal fractions
may not have an exact binary representation.  Therefore converting
from decimal to binary creates an error.

  1.020 => 1.00000101000111101011100001010001111010111000010100011110101110...
  1.025 => 1.00000110011001100110011001100110011001100110011001100110011001...
  1.030 => 1.00000111101011100001010001111010111000010100011110101110000101...

Since there are an infinite number of digits needed this can't be used
without reducing the number of digits in the calculation.  That means
that floating point operations routinely introduce errors into
calculations.  As to the exact details of rounding fractional digits I
have always avoided needing to know that information.  I have avoided
it because when I needed an exact value I knew about the problems and
instead used scaled integers.  And so I can't give a better
explanation now.  Only a recommendation to avoid it too.

Remember that floating point is only an approximation of reality.  For
more exact answers you may want to use binary coded decimal or scaled
integers.  This example is a good one to show why floating point isn't
used for monetary transactions.  My personal history is with CAD/EDA
software and in that problem domain most software uses scaled integers
to avoid these types of errors.

There have been recent changes in the way PHP handles rounding.
The upstream PHP documentation lists:

  5.3.0  The mode parameter was introduced.

  5.2.7  The inner workings of round() was changed to conform to the
         C99 standard.

In 5.3.0 and later (Sid or Squeeze) the mode parameter gives you some
control over rounding.

  $ php -r 'echo round(1.025,2,PHP_ROUND_HALF_UP) . "\n";'
  1.03

Also the default rounding mode has changed too.

  $ php -r 'echo round(1.025,2) . "\n";'
  1.03

Hope that helps,
Bob

P.S. Here are some useful references.

  http://en.wikipedia.org/wiki/Double_precision_floating-point_format

  http://en.wikipedia.org/wiki/Floating_point#Rounding_modes

  http://en.wikipedia.org/wiki/Binary_numeral_system#Decimal

  http://en.wikipedia.org/wiki/Binary_numeral_system#Representing_real_numbers

  http://cs.furman.edu/digitaldomain/more/ch6/dec_frac_to_bin.htm

  http://www.exploringbinary.com/binary-converter/





More information about the pkg-php-maint mailing list