Posting to newsgroup php.internals, you wrote:

> [...]
> round(), on the other hand, does more than the standard C functions do:
> With the additional places argument, it allows to round not only to
> integers but to arbitrary places. And this is precisely the thing that
> causes all the problems. ;-)

Agree, all the problems you pointed out are due to the different representation
of the floating point numbers ("float" for short) against the decimal numbers
we humans are used to write.

A "float" is typically a 53 bits number (IEEE 754 double precision) that can
always be represented exactly as decimal.

But the vice-versa it not true, and also very simple decimal numbers do not
have a finite "float" rapresentation and must be trunked or rounded. So for
example the number 0.1(dec) takes infinite decimal binary gigit once
converted to float:

0.1(dec) = 0.00011001100110011001100110011001100110011...(bin)

Since IEEE 754 double-precision numbers hold 53 bits, this value has to be
rounded to

0.00011001100110011001100110011001100110011001100110011010(bit)

(note the rounding on the last 2 binary digits). This latter value is what will
be stored in the $f variable after a statement like this one:

$f = 0.1;

Note too that the value actually stored in $f differs from that we may expect
simply reading the source: the difference is very small, but it exists. "Float"
values can always be converted back in decimal base with exact precision, so
for example in our case the $f variable now contains exactly

$f == 0.1000000000000000055511151231257827021181583404541015625(dec)

No surprise on that: the "error" was introduced by the PHP parser itself
when the statement "$f = 0.1;" was encountered the first time.

Now going back to the round() function, it is completely misleading to
analize statements like this one

echo round(0.285, 2);

because here we have actually two conversion problems:

    1. The "0.285" decimal number must be converted to "float" at the parse
    stage of the PHP interpreter, and that brings to "unexpected" results
    due to the rounding performed in this conversion.

    2. The round() function gets a number that already differs from that we
    see in the source, then it perfoms its calculations thinking at the
    number as it were a decimal number (multiplying by a power of 10,
    adding a rounding term, and then trunking the result), and then
    performs a division by a negative power of 10 that cannot be
    represented exactly with a float and that must be rounded before the
    division be performed, and then the result of the division must be
    rounded again to finally give the result of the function.

All these dec-to-bin conversions and "float" roundings sometimes bring to
unexpected results as already pointed out in the 42294 bug: we (humans)
continue to think at numbers like 0.285 as it they were decimals, but these
numbers are actually handled in a completely different base inside the
computer.

The only real bug is the round() function itself, and number_format() or
printf() should be used instead, because these function first convert the
binary float to decimal, and then perform the rounding over the decimal number.
But remember that also these functions operates on floats, i.e. numbers that
already may differ from those we see in the PHP source, so also with these
function we can see "strange" results.

If we really want a round() function, this function should operate just like
number_format() and printf() already do, i.e. returning a string, not a double,
or the second argument of the function should be "deprecated" or dropped at
all.

A final note: programmers concerned with rounding problems and looking for
"exact" results while dealing with floating point numbers are usually
erroneusly using "float" values to store monetary values as if PHP were a desk
calculator, rather than use BCMath or other specialized high-precision library
suitable for business applications.

Just my 0.019999999999 Euros.

Regards,
 ___ 
/_|_\  Umberto Salsi
\/_\/  www.icosaedro.it


-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to