Hi!

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.

Yes, sure. But IEEE 754 guarantees that the first 15 significant decimal
digits are correct (for double precision anyway), and in your example,
the first 17 significant decimal digits are in fact correct.

So, if you always round to the first 15 significant decimal digits when
outputting the floats, you always get correct results within that
decimal precision.

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.

Look it from this perspective: If you do echo 0.285 in current PHP, it
will output 0.285. This is due to the 'precision' ini setting (default
is 14) that implicitly rounds the result. And within those 14 decimal
digits, the results ARE guaranteed to be exact by IEEE 754.

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.

Not entirely true. ;-) number_format() uses round internally (math.c,
PHP 5.3 branch, line 944).

And, sprintf ("%.2f", 0.285) == "0.28" btw. - because even if you
convert the float back to a full decimal number, you get
0.284999999...something, so converting to string and rounding then
doesn't work either (at least not as a user would expect). This is the
reason why C for example doesn't bother to provide a round() function
for arbitrary precision, it can only round to integer which always can
be represented in an exact way.

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,

The problem is not returning a double. For example, literal 0.285 in the
source and the result of the expression round (0.2846, 3) are the same
floats. They are not exactly 0.285 but they are represented in the same
way (assuming the FPU mode thing gets added, see my first mail).

The actual problem is that the double you put in to round() is not
always what people think it is (as you already said). So letting round()
return a string() will not solve the underlying issues.

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.

The problem is that bcmath is not available everywhere - neither are
other such libraries. Also, using bcmath is quite cumberstone to read.
With floats you can do $a = $b + $c * $d + $e / $f - $g * $h / $j which
is very clear to read while with bcmath you have lots of nested function
calls which is really bad to read. And even if you have monetary
calculations: Normally, you are WELL within 14 decimal digits precision.

You also have to consider the following: If this were a proposal to
introduce a precision parameter into the round function, I would
completely agree that adding it will cause unecessary problems and
should not be done. But the fact is that it is already there in PHP and
you can't remove it with some serious BC breaks. And people are using
it. So may proposal is to make sure people get the best results
possible. (and since my proposal leaves some key issues open, what the
"best results possible" are is open to discussion, cf. fuzz vs. no-fuzz
etc.)

Anyway, I'll put my proposal into the wiki tomorrow with some additional
thoughts, perhaps my intentions are clearer then.

Regards,
Christian


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

Reply via email to