note 92888 added to ref.bc

From: 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

« previous php.notes (#159432) next »