Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:39193 Return-Path: Mailing-List: contact internals-help@lists.php.net; run by ezmlm Delivered-To: mailing list internals@lists.php.net Received: (qmail 62154 invoked from network); 22 Jul 2008 15:12:35 -0000 Received: from unknown (HELO lists.php.net) (127.0.0.1) by localhost with SMTP; 22 Jul 2008 15:12:35 -0000 Authentication-Results: pb1.pair.com smtp.mail=chris_se@gmx.net; spf=pass; sender-id=pass Authentication-Results: pb1.pair.com header.from=chris_se@gmx.net; sender-id=pass Received-SPF: pass (pb1.pair.com: domain gmx.net designates 213.165.64.20 as permitted sender) X-PHP-List-Original-Sender: chris_se@gmx.net X-Host-Fingerprint: 213.165.64.20 mail.gmx.net Linux 2.5 (sometimes 2.4) (4) Received: from [213.165.64.20] ([213.165.64.20:34742] helo=mail.gmx.net) by pb1.pair.com (ecelerity 2.1.1.9-wez r(12769M)) with ESMTP id AD/A6-46193-0E8F5884 for ; Tue, 22 Jul 2008 11:12:34 -0400 Received: (qmail invoked by alias); 22 Jul 2008 15:12:28 -0000 Received: from p54A16A22.dip.t-dialin.net (EHLO chris-se.dyndns.org) [84.161.106.34] by mail.gmx.net (mp030) with SMTP; 22 Jul 2008 17:12:28 +0200 X-Authenticated: #186999 X-Provags-ID: V01U2FsdGVkX1/q72YQa6vq0yFmHiQcuFCcDf6BPhmq50D6R7nWFe Pt6HYGuFvz3hsJ Received: from [192.168.100.13] (cobalt.seiler.lan [192.168.100.13]) by chris-se.dyndns.org (Postfix) with ESMTP id 755CC1AFC6; Tue, 22 Jul 2008 16:40:35 +0200 (CEST) Message-ID: <4885F88C.1080709@gmx.net> Date: Tue, 22 Jul 2008 17:11:08 +0200 User-Agent: Thunderbird 2.0.0.14 (X11/20080421) MIME-Version: 1.0 To: Umberto Salsi CC: php-dev List References: <4884D7BF.909@gmx.net> <200807221354.m6MDsXHL003953@icosaedro.it> In-Reply-To: <200807221354.m6MDsXHL003953@icosaedro.it> X-Enigmail-Version: 0.95.6 Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 7bit X-Y-GMX-Trusted: 0 X-FuHaFi: 0.5 Subject: Re: [PHP-DEV] Re: Rounding in PHP and floating point numbers in general From: chris_se@gmx.net (Christian Seiler) 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