note 92888 added to ref.bc
| From: | francois dot barbier at gmail dot com | Date: | Thu, 13 Aug 2009 05:19:19 +0000 |
| Subject: | note 92888 added to ref.bc | ||
| Groups: | php.notes | ||
| Request: | Send a blank email to php-notes+get-159432@lists.php.net to get a copy of this message | ||
As "benjcarson at digitaljunkies dot ca" (http://www.php.net/ref.bc.php#23038) noted in
the first two comments, bcmath doesn't accept exponential notation.
Moreover, you might have other problems if you feed the bcmath functions directly with floating
point numbers.
Consider the following example:
<?php
bcscale(1);
$a = 0.8;
$b = 0.7;
var_dump((string) $a); // string(3) "0.8"
var_dump((string) $b); // string(3) "0.a"
var_dump(bcadd($a, $b)); // string(3) "1.5"
setLocale(LC_ALL, 'fr_BE.UTF-8');
var_dump((string) $a); // string(3) "0,8" --> note the comma
var_dump((string) $b); // string(3) "0,7" --> note the comma
var_dump(bcadd($a, $b)); // string(3) "0.0"
?>
The floating point numbers passed to the bcadd() function are automatically converted to string
using the localized decimal separator. However, the bcmath functions always use a full stop, which
results in the last result being incorrect.
Below is a function to convert floating point numbers to strings correctly. It takes care of the
decimal separator and the exponential notation. It also preserve the precision without drifting away
(e.g. 1.0 doesn't become 0.99999...)
<?php
/**
* Convert a number to locale independent string without E notation and without
* loosing precision
*
* @param int/float/double $fNumber The number to convert.
* @return string The locale independent converted number.
*/
function bcconv($fNumber)
{
$sAppend = '';
$iDecimals = ini_get('precision') - floor(log10(abs($fNumber)));
if (0 > $iDecimals)
{
$fNumber *= pow(10, $iDecimals);
$sAppend = str_repeat('0', -$iDecimals);
$iDecimals = 0;
}
return number_format($fNumber, $iDecimals, '.', '').$sAppend;
}
?>
Example:
<?php
setLocale(LC_ALL, 'fr_BE.UTF-8'); // decimal separator is now a comma
$precision = ini_get('precision') + 2; // should give 16
bcscale($precision);
$big = pow(10, $precision);
$small = 1 / $big;
var_dump(bcconv($big + $small)); // string(17) "10000000000000000"
var_dump(bcadd($big, $small)); // string(18) "0.0000000000000000"
var_dump(bcadd(bcconv($big), bcconv($small))); // string(34)
"10000000000000000.0000000000000001"
?>
The first result's precision loss is due to PHP's internal floating point numbers'
representation.
The second result is wrong because of the localized decimal separator.
Finally, the last result is correct.
----
Server IP: 209.41.74.194
Probable Submitter: 87.65.121.159
----
Manual Page -- http://www.php.net/manual/en/ref.bc.php
Edit -- https://master.php.net/note/edit/92888
Del: integrated -- https://master.php.net/note/delete/92888/integrated
Del: useless -- https://master.php.net/note/delete/92888/useless
Del: bad code -- https://master.php.net/note/delete/92888/bad+code
Del: spam -- https://master.php.net/note/delete/92888/spam
Del: non-english -- https://master.php.net/note/delete/92888/non-english
Del: in docs -- https://master.php.net/note/delete/92888/in+docs
Del: other reasons-- https://master.php.net/note/delete/92888
Reject -- https://master.php.net/note/reject/92888
Search -- https://master.php.net/manage/user-notes.php