浮点数精度

这个问题在这里已经有了答案:

  • 浮点数学是否被破坏? 23个答案

  • 由于浮点运算!=实数运算。 对于一些花车ab ,由于不精确造成的差异的例子是(a+b)-b != a 。 这适用于使用浮动的任何语言。

    由于浮点数是有限精度的二进制数,因此有一定数量的可表示数,这会导致精度问题和这样的意外。 这里有另一个有趣的阅读:每个计算机科学家应该知道什么是浮点运算。


    回到你的问题,基本上没有办法准确地表示二进制的34.99或0.01(就像在十进制中,1/3 = 0.3333 ...),所以使用近似值代替。 为了解决这个问题,你可以:

  • round($result, 2)使用round($result, 2)将其舍入到2个小数位。

  • 使用整数。 如果这就是货币,比如说美元,那么存储35.00美元为3500和34.99美元为34.99美元,然后将结果除以100。

  • 很遗憾PHP没有像其他语言那样的十进制数据类型。


    像所有数字一样,浮点数字必须以0和1的字符串形式存储在内存中。 这一切都是计算机的一部分。 浮点与整数的不同之处在于我们如何解释0和1,当我们想看看它们时。

    一位是“符号”(0 =正数,1 =负数),8位是指数(范围从-128到+127),23位是被称为“尾数”(分数)的数字。 所以(S1)(P8)(M23)的二进制表示具有值(-1 ^ S)M * 2 ^ P

    “尾数”呈特殊形式。 在正常的科学记数法中,我们显示“一个地方”以及分数。 例如:

    4.39×10 ^ 2 = 439

    在二进制中,“一个地方”是一个单一的位。 由于我们忽略了所有最左侧的0(科学记数法)(我们忽略了任何微不足道的数字),所以第一位保证是1

    1.101×2 ^ 3 = 1101 = 13

    由于我们保证第一位将是1,所以在存储编号以节省空间时删除此位。 所以上面的数字存储为101(尾数)。 假设领先1

    作为一个例子,我们来看看二进制字符串

    00000010010110000000000000000000
    

    打破它的组件:

    Sign    Power           Mantissa
     0     00000100   10110000000000000000000
     +        +4             1.1011
     +        +4       1 + .5 + .125 + .0625
     +        +4             1.6875
    

    应用我们简单的公式:

    (-1^S)M*2^P
    (-1^0)(1.6875)*2^(+4)
    (1)(1.6875)*(16)
    27
    

    换句话说,00000010010110000000000000000000的浮点数是27(根据IEEE-754标准)。

    但是,对于许多数字,没有确切的二进制表示。 很像1/3 = 0.333 ....重复一次,1/100是0.00000010100011110101110000 .....重复“10100011110101110000”。 但是,32位计算机无法将整个数字存储在浮点数中。 所以这是最好的猜测。

    0.0000001010001111010111000010100011110101110000
    
    Sign    Power           Mantissa
     +        -7     1.01000111101011100001010
     0    -00000111   01000111101011100001010
     0     11111001   01000111101011100001010
    01111100101000111101011100001010
    

    (注意,使用2的补码产生负7)

    应该立刻清楚01111100101000111101011100001010看起来不像0.01

    但更重要的是,它包含一个重复小数的截断版本。 原始小数包含重复的“10100011110101110000”。 我们已将此简化为01000111101011100001010

    通过我们的公式将这个浮点数转换回十进制数,我们得到0.0099999979(注意这是一个32位计算机,一个64位计算机会有更高的精度)


    这里有很多关于为什么浮点数的工作方式的答案...

    但几乎没有任何精确的说法(Pickle提到它)。 如果你想(或者需要)精确的精度,唯一的方法就是使用BC Math扩展(这实际上只是一个BigNum,任意精度实现......)。

    要添加两个数字:

    $number = '12345678901234.1234567890';
    $number2 = '1';
    echo bcadd($number, $number2);
    

    将导致12345678901235.1234567890 ...

    这被称为任意精度数学。 基本上所有的数字都是为每个操作分析的字符串,并且操作是按照数字的方式进行的(考虑长分区,但是由库完成)。 所以这意味着它很慢(与常规的数学结构相比)。 但它非常强大。 你可以乘,加,减,除,寻找模和幂的任何数字,具有精确的字符串表示形式。

    所以你不能以100%的精度做1/3 ,因为它有一个重复的小数(因此是不合理的)。

    但是,如果你想知道1500.0015平方是多少:

    使用32位浮点数(双精度)给出估计结果:

    2250004.5000023
    

    但是,bcmath给出了确切的答案:

    2250004.50000225
    

    这一切都取决于你需要的精度。

    此外,在这里要注意的其他事项。 PHP只能表示32位或64位整数(取决于您的安装)。 所以如果一个整数超过本地int类型的大小(32位为21亿,9.2 x10 ^ 18或92亿十亿),PHP会将int转换为浮点数。 虽然这不是一个问题(因为所有小于系统浮点数的精度都可以直接表示为浮点数),但如果将两个数相乘,它将失去显着的精度。

    例如,给定$n = '40000000002'

    作为一个数字, $n将是float(40000000002) ,这是正确的,因为它是完全表示的。 但是,如果我们将其平方,我们得到: float(1.60000000016E+21)

    作为一个字符串(使用BC数学), $n将完全是'40000000002' 。 如果我们平方,我们得到: string(22) "1600000000160000000004" ...

    因此,如果您需要大数字的精度或有理数的小数点,您可能需要查看bcmath ...

    链接地址: http://www.djcxy.com/p/27439.html

    上一篇: Floating Number Precision

    下一篇: Is double Multiplication Broken in .NET?