文字

Float 浮点型

浮点型(也叫浮点数 float,双精度数 double 或实数 real)可以用以下任一语法定义:

<?php
$a 
1.234
$b  1.2e3
$c  7E-10 ;
?>

浮点数的形式表示:

LNUM          [0-9]+
DNUM          ([0-9]*[\.]{LNUM}) | ({LNUM}[\.][0-9]*)
EXPONENT_DNUM [+-]?(({LNUM} | {DNUM}) [eE][+-]? {LNUM})

浮点数的字长和平台相关,尽管通常最大值是 1.8e308 并具有 14 位十进制数字的精度(64 位 IEEE 格式)。

Warning

浮点数的精度

浮点数的精度有限。尽管取决于系统,PHP 通常使用 IEEE 754 双精度格式,则由于取整而导致的最大相对误差为 1.11e-16。非基本数学运算可能会给出更大误差,并且要考虑到进行复合运算时的误差传递。

此外,以十进制能够精确表示的有理数如 0.10.7,无论有多少尾数都不能被内部所使用的二进制精确表示,因此不能在不丢失一点点精度的情况下转换为二进制的格式。这就会造成混乱的结果:例如,floor((0.1+0.7)*10) 通常会返回 7 而不是预期中的 8,因为该结果内部的表示其实是类似 7.9999999999999991118...

所以永远不要相信浮点数结果精确到了最后一位,也永远不要比较两个浮点数是否相等。如果确实需要更高的精度,应该使用任意精度数学函数或者 gmp 函数。

参见» 浮点数指南网页的简单解释。

转换为浮点数

如果希望了解有关何时和如何将字符串转换成浮点数的信息,请参阅“字符串转换为数值”一节。对于其它类型的值,其情况类似于先将值转换成整型,然后再转换成浮点。请参阅“转换为整型”一节以获取更多信息。自 PHP 5 起,如果试图将对象转换为浮点数,会发出一条 E_NOTICE 错误消息。

比较浮点数

如上述警告信息所言,由于内部表达方式的原因,比较两个浮点数是否相等是有问题的。不过还是有迂回的方法来比较浮点数值的。

要测试浮点数是否相等,要使用一个仅比该数值大一丁点的最小误差值。该值也被称为机器极小值(epsilon)或最小单元取整数,是计算中所能接受的最小的差别值。

$a $b 在小数点后五位精度内都是相等的。

<?php
$a 
1.23456789 ;
$b  1.23456780 ;
$epsilon  0.00001 ;

if(
abs ( $a - $b ) <  $epsilon ) {
    echo 
"true" ;
}
?>

NaN

某些数学运算会产生一个由常量 NAN 所代表的结果。此结果代表着一个在浮点数运算中未定义或不可表述的值。任何拿此值与其它任何值进行的松散或严格比较的结果都是 FALSE

由于 NAN 代表着任何不同值,不应拿 NAN 去和其它值进行比较,包括其自身,应该用 is_nan() 来检查。

用户评论:

[#1] luizvid at gmail dot com [2015-05-04 00:06:36]

An effective way to compare two real numbers (including floating point numbers) with high accuracy and still be able to set precision is using the BC Math function bccomp();

For instance:
<?php
    $a 
1.23456789;
    
$b 1.23456780;
    
$precision 5;

    if(
bccomp($a$b$precision)  === ) {
        echo 
"true";
    } 
// true
?>

<?php
    $a 
sprintf('%.17f'0.1+0.2);
    
$b 0.3;

    if(
bccomp($a$b17)  !== ) {
        echo 
"FALSE";
    } 
// FALSE
?>

[#2] nathanb at php dot net [2015-04-03 17:45:40]

An extremely small and simple example of this is:
<?php
  
echo intval(19.31 100);
?>

[#3] Adam H [2014-10-03 14:35:31]

I've just come across this issue with floats when writing a function for pricing. When converting from string to a float, with 2 digits of precision, the issue with comparing floats can pop up and give inconsistent results due to the conversion process.

An easier way rather than relying on the mentioned epsilon method is to use number_format (at least for me as I'll remember it!).

Example function that can return an unexpected result:

if((float)$a == (float)$b) {
echo true;
} else {
echo false;
}

echo's false in this example.

Using number format here to trim down the precision (2 point precision being mostly used for currencies etc, although higher precisions should be correctly catered for by number_format), will return an expected result:

if(number_format((float)$a, 2) == number_format((float)$b, 2)) {
echo true;
} else {
echo false;
}

Correctly echo's true.

[#4] catalin dot luntraru at gmail dot com [2013-11-18 15:44:31]

$x = 8 - 6.4;  // which is equal to 1.6
$y = 1.6;
var_dump($x == $y); // is not true

PHP thinks that 1.6 (coming from a difference) is not equal to 1.6. To make it work, use round()

var_dump(round($x, 2) == round($y, 2)); // this is true

This happens probably because $x is not really 1.6, but 1.599999.. and var_dump shows it to you as being 1.6.

[#5] jack at surfacefinishtech dot com [2013-01-25 19:40:11]

<?php

function hex2float($number) {
    
$binfinal sprintf("%032b",hexdec($number));
    
$sign substr($binfinal01);
    
$exp substr($binfinal18);
    
$mantissa "1".substr($binfinal9);
    
$mantissa str_split($mantissa);
    
$exp bindec($exp)-127;
    
$significand=0;
    for (
$i 0$i 24$i++) {
        
$significand += (pow(2,$i))*$mantissa[$i];
    }
    return 
$significand pow(2,$exp) * ($sign*-2+1);
}
?>

[#6] pcunha at gmail dot com [2012-10-23 14:12:17]

To simply convert 32 bits float from hex to float:

<?php
function hexfloat ($hex){
    return (
unpack("f"pack('H*',$hex))[1]);
}
?>


This may be useful for arduino interface with php.

[#7] davidszilardd at gmail dot com [2012-04-24 10:58:43]

The function returns 5 for 5,000 because if there is no decimal point, then the first strpos will be FALSE, and FALSE < 1 is TRUE so the condition will be still true.

It should be checked whether strpos returns a valid position:

<?php
function str2num($str)

      if (
strpos($str'.') !== FALSE && strpos($str,    ',') !== FALSE && strpos($str'.') < strpos($str,','))
          { 
            
$str str_replace('.','',$str); 
            
$str strtr($str,',','.');            
        } 
        else
        { 
            
$str str_replace(',','',$str);            
        } 
        
        return (float)
$str
}
?>

[#8] reinaldorock at yahoo dot com dot br [2011-10-13 19:12:11]

Convert locale string into float number

<?php
function str2num($str){
  if(
strpos($str'.') < strpos($str,',')){
            
$str str_replace('.','',$str);
            
$str strtr($str,',','.');            
        }
        else{
            
$str str_replace(',','',$str);            
        }
        return (float)
$str;
}

str2num('25,01'); //25.01
str2num('2.5,01'); //25.01
str2num('25.01'); //25.01
str2num('2,5.01'); //25.01
?>

[#9] zelko at mojeime dot com [2011-03-31 13:02:11]

<?php
   $binarydata32 
pack('H*','00000000');
   
$float32 unpack("f"$binarydata32); // 0.0

   
$binarydata64 pack('H*','0000000000000000');
   
$float64 unpack("d"$binarydata64); // 0.0
?>


I get 0 both for 32-bit and 64-bit numbers.

But, please don't use your own "functions" to "convert" from float to binary and vice versa. Looping performance in PHP is horrible. Using pack/unpack you use processor's encoding, which is always correct. In C++ you can access the same 32/64 data as either float/double or 32/64 bit integer. No "conversions".

To get binary encoding:
<?php
   $float32 
pack("f"5300231);
   
$binarydata32 =unpack('H*',$float32); //"0EC0A14A"

   
$float64 pack("d"5300231);
   
$binarydata64 =unpack('H*',$float64); //"000000C001385441"
?>


And my example from half a year ago:
<?php
    $binarydata32 
pack('H*','0EC0A14A');
    
$float32 unpack("f"$binarydata32); // 5300231
   
    
$binarydata64 pack('H*','000000C001385441');
    
$float64 unpack("d"$binarydata64); // 5300231
?>


And please mind the Big and Little endian boys...

[#10] manasseh at smartcomputerinc.com [2011-03-06 19:35:16]

I found that 00000000 hex was converting to 1.0 decimal. From the Wikipedia article on IEEE-754 floating point:

The true significand includes 23 fraction bits to the right of the binary point and an implicit leading bit (to the left of the binary point) with value 1 unless the exponent is stored with all zeros.

In hex2float32n, replace:

      $intnumber=bindec("1".$binint);

with

   if ($exp <> -127)
      { $intnumber=bindec("1".$binint); };

and then 00000000 works correctly without affecting "normal" numbers.

[#11] Julian L [2010-12-09 07:51:00]

Convert a hex string into a 32-bit IEEE 754 float number.  This function is 2 times faster then the below hex to 32bit function.  This function only changes datatypes (string to int) once. Also, this function is a port from the hex to 64bit function from below.

<?php
function hexTo32Float($strHex) {
    
$v hexdec($strHex);
    
$x = ($v & ((<< 23) - 1)) + (<< 23) * ($v >> 31 1);
    
$exp = ($v >> 23 0xFF) - 127;
    return 
$x pow(2$exp 23);
}
?>


<?php
//example
echo hexTo32Float("C4028000"); // outputs: -522
echo hexTo32Float("457F9000"); // outputs: 4089
echo hexTo32Float("2D7F5");    // outputs: 6.00804264307E-39
echo hexTo32Float("0002D7F5"); // outputs: 6.00804264307E-39
echo hexTo32Float("47D9F95E"); // outputs: 111602.734375
?>

[#12] zelko at mojeime dot com [2010-06-05 13:43:46]

The was talk about "converting" 32 and 64 bit IEEE754 binary numbers to PHP float. The issue isn't as much converting, since they are already in binary form, as it is casting. PHP doesn't allow direct accessing of memory, but you can still get around a bit.

The right was to read floats (32 and 64 bit) is this:

<?php
    $binarydata32 
pack('H*','0EC0A14A');
    
$float32 unpack("f"$binarydata32);
    
    
$binarydata64 pack('H*','000000C001385441');
    
$float64 unpack("d"$binarydata64);
    
    
var_dump($float32,$float64,$float32==$float64);   
?>


The result of dump():
<?php
 
array(1) {
  [
1]=>
  
float(5300231)
}
array(
1) {
  [
1]=>
  
float(5300231)
}
bool(true)
?>


Note: mind the Big and Little endian boys

[#13] magicaltux at php dot net [2010-06-01 19:02:14]

In some cases you may want to get the maximum value for a float without getting "INF".

var_dump(1.8e308); will usually show: float(INF)

I wrote a tiny function that will iterate in order to find the biggest non-infinite float value. It comes with a configurable multiplicator and affine values so you can share more CPU to get a more accurate estimate.

I haven't seen better values with more affine, but well, the possibility is here so if you really thing it's worth the cpu time, just try to affine more.

Best results seems to be with mul=2/affine=1. You can play with the values and see what you get. The good thing is this method will work on any system.

<?php
  
function float_max($mul 2$affine 1) {
    
$max 1$omax 0;
    while((string)
$max != 'INF') { $omax $max$max *= $mul; }

    for(
$i 0$i $affine$i++) {
      
$pmax 1$max $omax;
      while((string)
$max != 'INF') {
        
$omax $max;
        
$max += $pmax;
        
$pmax *= $mul;
      }
    }
    return 
$omax;
  }
?>

[#14] inforsci at gmail dot com [2010-04-27 23:48:18]

convert 32bit HEX values into IEEE 754 floating point 
<?php 

$strHex 
"C45F82ED"

$bin str_pad(base_convert($strHex162), 32"0"STR_PAD_LEFT); 
$sign $bin[0]; 
$exp bindec(substr($bin18)) - 127
$man = (<< 22) + bindec(substr($bin923)); 

$dec $man pow(2$exp 23) * ($sign ? -1); 

echo 
"Answer = " $dec "<BR>\n"

?>

[#15] Anonymous [2010-04-25 23:48:48]

Calculations involving float types become inaccurate when it deals with numbers with more than approximately 8 digits long where ever the decimal point is.  This is because of how 32bit floats are commonly stored in memory.  This means if you rely on float types while working with tiny fractions or large numbers, your calculations can end up between tiny fractions to several trillion off.

This usually won't matter when converting to binary memory storage form and editing many applications' float memory addresses directly, or dealing with smaller length numbers.  But if you're working with larger scale numbers and decimals, it's best to switch to working with other types: http://www.php.net/manual/en/refs.math.php

[#16] pgarvin76+phpmanual at gmail dot com [2010-01-12 19:36:27]

When converting from float to string trailing zeros will be dropped. Consider the following example. Tested on PHP 5.3.1.

<?php
$a 
5.00500;
$b 30.00;
echo 
"a = $a\n";
echo 
"b = $b\n";

?>

[#17] francois dot barbier at gmail dot com [2009-08-12 20:49:28]

As "m dot lebkowski+php at gmail dot com" (http://www.php.net/language.types.float#81416) noted 9 comments below :

When PHP converts a float to a string, the decimal separator used depends on the current locale conventions.

However, to declare a floating point number, one must always use a full stop otherwhise the code would be locale dependent (imagine the nightmare):
<?php
$float 
1.5;           // float(1.5)
$float 1,5;           // Parse error: syntax error, unexpected ','
$float = (float) '1.5'// float(1.5)
$float = (float) '1,5'// float(1)
?>


Now, if you have a string containing a localized number, you can convert it back to a floating point number using the following function:
<?php

function str2num($sNumber)
{
    
$aConventions localeConv();
    
$sNumber trim((string) $sNumber);
    
$bIsNegative = (=== $aConventions['n_sign_posn'] && '(' === $sNumber{0} && ')' === $sNumber{strlen($sNumber) - 1});
    
$sCharacters $aConventions['decimal_point'].
                   
$aConventions['mon_decimal_point'].
                   
$aConventions['negative_sign'];
    
$sNumber preg_replace('/[^'.preg_quote($sCharacters).'\d]+/'''trim((string) $sNumber));
    
$iLength strlen($sNumber);
    if (
strlen($aConventions['decimal_point']))
    {
        
$sNumber str_replace($aConventions['decimal_point'], '.'$sNumber);
    }
    if (
strlen($aConventions['mon_decimal_point']))
    {
        
$sNumber str_replace($aConventions['mon_decimal_point'], '.'$sNumber);
    }
    
$sNegativeSign $aConventions['negative_sign'];
    if (
strlen($sNegativeSign) && !== $aConventions['n_sign_posn'])
    {
        
$bIsNegative = ($sNegativeSign === $sNumber{0} || $sNegativeSign === $sNumber{$iLength 1});
        if (
$bIsNegative)
        {
            
$sNumber str_replace($aConventions['negative_sign'], ''$sNumber);
        }
    }
    
$fNumber = (float) $sNumber;
    if (
$bIsNegative)
    {
        
$fNumber = -$fNumber;
    }
    return 
$fNumber;
}
?>


Example:
<?php
setLocale
(LC_ALL'fr_BE.UTF-8'); // decimal separator is now a comma
$float = -123456.789;
$string = (string) $float;
var_dump($float);           // float(-123456,789)
var_dump($string);          // string(11) "-123456,789"
var_dump((float) $string);  // float(-123456)
var_dump(str2num($string)); // float(-123456,789)
?>


It also works with strings returned by the number_format() function:
<?php
setLocale
(LC_ALL'fr_BE.UTF-8'); // decimal separator is now a comma
$conv localeconv();
$float = -123456.789;
$string $conv['int_curr_symbol'].number_format($float$conv['frac_digits'], $conv['decimal_point'], $conv['thousands_sep']);
var_dump($float);           // float(-123456,789)
var_dump($string);          // string(15) "EUR -123.456,79"
var_dump((float) $string);  // float(0)
var_dump(str2num($string)); // float(-123456,79)
?>

[#18] rob at willo dot org [2009-06-02 21:18:46]

PHP will parse ".123" with no leading digit; just a decimal point. For a command-line example:

php -r "echo 1 + .123;"

The regular expression provided above does not parse it.
My correction is:

EXPONENT_DNUM = "[+-]?({DNUM} | ({LNUM} | {DNUM}) [eE][+-]? {LNUM})"

NOTE: {LNUM} by itself is an integer, not a floating point.

[#19] Bob [2009-05-07 22:04:18]

Here is a function to convert an exponential-format float to a decimal-format float; e.g. 1.6e+12 to 1600000000000.
It will help addressing the problem specified by kjohnson above.
I have tested it, but not in any real world situation so any feedback/improvements/bug-reports would be appreciated.
<?php
function exp_to_dec($float_str)
// formats a floating point number string in decimal notation, supports signed floats, also supports non-standard formatting e.g. 0.2e+2 for 20
// e.g. '1.6E+6' to '1600000', '-4.566e-12' to '-0.000000000004566', '+34e+10' to '340000000000'
// Author: Bob
{
    
// make sure its a standard php float string (i.e. change 0.2e+2 to 20)
    // php will automatically format floats decimally if they are within a certain range
    
$float_str = (string)((float)($float_str));

    
// if there is an E in the float string
    
if(($pos strpos(strtolower($float_str), 'e')) !== false)
    {
        
// get either side of the E, e.g. 1.6E+6 => exp E+6, num 1.6
        
$exp substr($float_str$pos+1);
        
$num substr($float_str0$pos);
        
        
// strip off num sign, if there is one, and leave it off if its + (not required)
        
if((($num_sign $num[0]) === '+') || ($num_sign === '-')) $num substr($num1);
        else 
$num_sign '';
        if(
$num_sign === '+'$num_sign '';
        
        
// strip off exponential sign ('+' or '-' as in 'E+6') if there is one, otherwise throw error, e.g. E+6 => '+'
        
if((($exp_sign $exp[0]) === '+') || ($exp_sign === '-')) $exp substr($exp1);
        else 
trigger_error("Could not convert exponential notation to decimal notation: invalid float string '$float_str'"E_USER_ERROR);
        
        
// get the number of decimal places to the right of the decimal point (or 0 if there is no dec point), e.g., 1.6 => 1
        
$right_dec_places = (($dec_pos strpos($num'.')) === false) ? strlen(substr($num$dec_pos+1));
        
// get the number of decimal places to the left of the decimal point (or the length of the entire num if there is no dec point), e.g. 1.6 => 1
        
$left_dec_places = ($dec_pos === false) ? strlen($num) : strlen(substr($num0$dec_pos));
        
        
// work out number of zeros from exp, exp sign and dec places, e.g. exp 6, exp sign +, dec places 1 => num zeros 5
        
if($exp_sign === '+'$num_zeros $exp $right_dec_places;
        else 
$num_zeros $exp $left_dec_places;
        
        
// build a string with $num_zeros zeros, e.g. '0' 5 times => '00000'
        
$zeros str_pad(''$num_zeros'0');
        
        
// strip decimal from num, e.g. 1.6 => 16
        
if($dec_pos !== false$num str_replace('.'''$num);
        
        
// if positive exponent, return like 1600000
        
if($exp_sign === '+') return $num_sign.$num.$zeros;
        
// if negative exponent, return like 0.0000016
        
else return $num_sign.'0.'.$zeros.$num;
    }
    
// otherwise, assume already in decimal notation and return
    
else return $float_str;
}
?>

[#20] Bob [2009-05-07 20:34:07]

In MySQL, many floating point number types can have a range specified using 2 values, the "precision" and the "scale" E.g. 'float(precision,scale)' for the datatype. This syntax means a number may be <precision> bits long, but may only have <scale> bits after the decimal point. E.g. a 'float(5,2)' field may have the values -999.99 to 999.99.
Here is a function to validate a PHP float using this syntax:
<?php
function validate_float($float$precision$scale)
{
    
$max = (float)str_pad(""$precision-$scale'9').'.'.str_pad(""$scale'9');
    
$min = (float)"-$max";

    if((
$float $min) || ($float $max)) return false;
    else return 
true;
}
?>

[#21] info at forrest79 dot net [2009-02-10 12:55:19]

My BIN to FLOAT (IEEE754), the first one doesn't work for me:

<?php
        
function binToFloat($bin) {
            if(
strlen($bin) > 32) {
                return 
false;
            } else if(
strlen($bin) < 32) {
                
$bin str_repeat('0', (32 strlen($bin))) . $bin;
            }

            
$sign 1;
            if(
intval($bin[0]) == 1) {
                
$sign = -1;
            }

            
$binExponent substr($bin18);
            
$exponent = -127;
            for(
$i 0$i 8$i++) {
                
$exponent += (intval($binExponent[$i]) * pow(2$i));
            }

            
$binBase substr($bin9);            
            
$base 1.0;
            for(
$x 0$x 23$x++) {
                
$base += (intval($binBase[$x]) * pow(0.5, ($x 1)));
            }

            
$float = (float) $sign pow(2$exponent) * $base;

            return 
$float;
        }
?>

[#22] kjohnson at zootweb dot com [2008-06-02 12:23:59]

PHP switches from the standard decimal notation to exponential notation for certain "special" floats. You can see a partial list of such "special" values with this:

<?php
for( $tmp 0$i 0$i 100$i++ ) {
    
$tmp += 100000;
    echo 
round($tmp),"\n";
}
?>


So, if you add two floats, end up with a "special" value, e.g. 1.2E+6, then put that value unmodified into an update query to store the value in a decimal column, say, you will likely get a failed transaction, since the database will see "1.2E+6" as varchar data, not decimal. Likewise, you will likely get an XSD validation error if you put the value into xml.

I have to be honest: this is one of the strangest things I have seen in any language in over 20 years of coding, and it is a colossal pain to work around.

[#23] m dot lebkowski+php at gmail dot com [2008-02-27 01:18:51]

Just another note about the locales. Consider the following code:

<?php 
    
// in polish locale decimal separator is ","
    
setlocale(LC_ALL"pl_PL");
    
$a 5/2;
    echo (float)(string)
$a;
    
/// prints "2", so the decimal part is dropped
?>


This causes very serious problems in my opinion. In some locale combination the typecasting can be destructive.
Maybe when locale decimal separator is ",", then (float)"2,5" should be recognized as "two and a half"? 
Anyway - bare that in mind and be very careful when casting floats to strings and back.

[#24] helly at php dot net [2007-10-22 12:10:58]

Floating point values have a limited precision. Hence a value might not have the same string representation after any processing. That also includes writing a floating point value in your script and directly printing it without any mathematical operations.

If you would like to know more about "floats" and what IEEE 754 is read this: http://docs.sun.com/source/806-3568/ncg_goldberg.html

[#25] Luzian [2005-11-17 00:03:30]

Be careful when using float values in strings that are used as code later, for example when generating JavaScript code or SQL statements. The float is actually formatted according to the browser's locale setting, which means that "0.23" will result in "0,23". Imagine something like this:

$x = 0.23;
$js = "var foo = doBar($x);";
print $js;

This would result in a different result for users with some locales. On most systems, this would print:

var foo = doBar(0.23);

but when for example a user from Germany arrives, it would be different:

var foo = doBar(0,23);

which is obviously a different call to the function. JavaScript won't state an error, additional arguments are discarded without notice, but the function doBar(a) would get 0 as parameter. Similar problems could arise anywhere else (SQL, any string used as code somewhere else). The problem persists, if you use the "." operator instead of evaluating the variable in the string.

So if you REALLY need to be sure to have the string correctly formatted, use number_format() to do it!

[#26] TRI0N [2005-09-23 19:01:29]

Here is a simple formula to break down a number and get rid of the decimal values.  I built this to take a number in seconds and convert it to a readable value for Server Uptimes.

<?php
$day 
floor(($uptime 86400)*1.0) ;
$calc1 $day 86400 ;
$calc2 $uptime $calc1 ;
$hour floor(($calc2 3600)*1.0) ;
if (
$hour 10) {
$hour "0".$hour ;
}
$calc3 $hour 3600 ;
$calc4 $calc2 $calc3 ;
$min floor(($calc4 60)*1.0) ;
if (
$min 10) {
$min "0".$min ;
}
$calc5 $min 60 ;
$sec floor(($calc4 $calc5)*1.0) ;
if (
$min 10) {
$sec "0".$sec ;
}
$uptime2 $day." Days, ".$hour.":".$min.":".$sec ;
?>


Place this where you want the results to be seen:
<?php echo $uptime2 ?>

For a Value of 1455587 seconds the results will show as followed:
16 Days, 20:19:47

Enjoy

[#27] rick at ninjafoo dot com [2005-07-06 01:04:05]

Concider the following:

(19.6*100) != 1960  

echo gettype(19.6*100) returns 'double', However even ..... 

(19.6*100) !== (double)1960 

19.6*100 cannot be compaired to anything without manually 
casting it as something else first. 

(string)(19.6*100) == 1960

Rule of thumb, if it has a decimal point, use the BCMath functions.

[#28] feline at NOSPAM dot penguin dot servehttp dot com [2004-08-12 18:36:26]

General computing hint: If you're keeping track of money, do yourself and your users the favor of handling everything internally in cents and do as much math as you can in integers. Store values in cents if at all possible. Add and subtract in cents. At every operation that wii involve floats, ask yourself "what will happen in the real world if I get a fraction of a cent here" and if the answer is that this operation will generate a transaction in integer cents, do not try to carry fictional fractional accuracy that will only screw things up later.

[#29] james dot cridland at virginradio dot co dot uk [2003-04-28 07:44:07]

The 'floating point precision' box in practice means:

<?php echo (69.1-floor(69.1)); ?>
Think this'll return 0.1?
It doesn't - it returns 0.099999999999994

<?php echo round((69.1-floor(69.1))); ?>
This returns 0.1 and is the workaround we use.

Note that
<?php echo (4.1-floor(4.1)); ?>
*does* return 0.1 - so if you, like us, test this with low numbers, you won't, like us, understand why all of a sudden your script stops working, until you spend a lot of time, like us, debugging it.

So, that's all lovely then.

[#30] dev at maintainfit dot com [2003-04-15 11:27:35]

I was programming an accounting application in MySql that required me to sum a collection of floats and ensure that they equal zero before commiting a transaction, but as seen above a sum of floats cannot always be trusted (as was my case).  I kept getting a very small remainder (like 1.4512431231e-14).  Since I had used number_format(num,2) to set the precision of the numbers in the database to only two (2) decimal places, when the time comes to calculate the sum I simply multiply every number by ten (10), therby eliminating and decimal places and leaving me with integers to preform my sum.  This worked great.

[#31] backov at spotbrokers-nospamplz dot com [2003-03-05 13:16:44]

I'd like to point out a "feature" of PHP's floating point support that isn't made clear anywhere here, and was driving me insane.

This test (where var_dump says that $a=0.1 and $b=0.1)

if ($a>=$b) echo "blah!";

Will fail in some cases due to hidden precision (standard C problem, that PHP docs make no mention of, so I assumed they had gotten rid of it). I should point out that I originally thought this was an issue with the floats being stored as strings, so I forced them to be floats and they still didn't get evaluated properly (probably 2 different problems there).

To fix, I had to do this horrible kludge (the equivelant of anyway):

if (round($a,3)>=round($b,3)) echo "blah!";

THIS works. Obviously even though var_dump says the variables are identical, and they SHOULD BE identical (started at 0.01 and added 0.001 repeatedly), they're not. There's some hidden precision there that was making me tear my hair out. Perhaps this should be added to the documentation?

[#32] www.sarioz.com [2003-02-04 22:49:03]

just a comment on something the "Floating point precision" inset, which goes: "This is related to .... 0.3333333."

While the author probably knows what they are talking about, this loss of precision has nothing to do with decimal notation, it has to do with representation as a floating-point binary in a finite register, such as while 0.8 terminates in decimal, it is the repeating 0.110011001100... in binary, which is truncated.  0.1 and 0.7 are also non-terminating in binary, so they are also truncated, and the sum of these truncated numbers does not add up to the truncated binary representation of 0.8 (which is why (floor)(0.8*10) yields a different, more intuitive, result).  However, since 2 is a factor of 10, any number that terminates in binary also terminates in decimal.

上一篇: 下一篇: