gpt4 book ai didi

floating-point - 为什么 float 不准确?

转载 作者:行者123 更新时间:2023-12-03 04:13:44 28 4
gpt4 key购买 nike

为什么有些数字在存储为浮点数时会失去准确性?

例如,十进制数 9.2可以精确地表示为两个十进制整数的比率 ( 92/10 ),这两个整数都可以精确地表示为二进制 ( 0b1011100/0b1010 )。但是,存储为浮点数的相同比率永远不会完全等于 9.2 :

32-bit "single precision" float: 9.19999980926513671875
64-bit "double precision" float: 9.199999999999999289457264239899814128875732421875

这么一个看似简单的数字怎么会“太大”而无法用 来表达64 位 内存?

最佳答案

在大多数编程语言中,浮点数的表示方式很像 scientific notation : 带有指数和尾数(也称为有效数)。一个非常简单的数字,比如 9.2 , 实际上是这个分数:

5179139571476070 * 2 -49



其中指数是 -49尾数是 5179139571476070 .不能以这种方式表示一些十进制数的原因是指数和尾数都必须是整数。换句话说,所有浮点数必须是整数乘以 2 的整数次幂。
9.2可能只是 92/10 ,但是 10 不能表示为 2n 如果 仅限于整数值。

查看数据

首先,使用几个函数来查看构成 32 位和 64 位 float 的组件.如果您只关心输出(Python 中的示例),请忽略这些:

def float_to_bin_parts(number, bits=64):
if bits == 32: # single precision
int_pack = 'I'
float_pack = 'f'
exponent_bits = 8
mantissa_bits = 23
exponent_bias = 127
elif bits == 64: # double precision. all python floats are this
int_pack = 'Q'
float_pack = 'd'
exponent_bits = 11
mantissa_bits = 52
exponent_bias = 1023
else:
raise ValueError, 'bits argument must be 32 or 64'
bin_iter = iter(bin(struct.unpack(int_pack, struct.pack(float_pack, number))[0])[2:].rjust(bits, '0'))
return [''.join(islice(bin_iter, x)) for x in (1, exponent_bits, mantissa_bits)]

该函数背后有很多复杂性,解释起来很切题,但如果您有兴趣,我们的目的的重要资源是 struct模块。

Python的 float是一个 64 位的 double 数。在 C、C++、Java 和 C# 等其他语言中, double 具有单独的类型 double ,通常实现为 64 位。

当我们用我们的例子调用这个函数时, 9.2 ,这是我们得到的:
>>> float_to_bin_parts(9.2)
['0', '10000000010', '0010011001100110011001100110011001100110011001100110']

解释数据

您会看到我已将返回值拆分为三个部分。这些组件是:
  • 签到
  • 指数
  • 尾数(也称为有效数或分数)

  • 符号

    符号作为单个位存储在第一个组件中。很容易解释: 0表示浮点数是正数; 1意味着它是负面的。因为 9.2为正,我们的符号值为 0 .

    指数

    指数作为 11 位存储在中间组件中。在我们的例子中, 0b10000000010 .以十进制表示,表示值 1026 .这个组件的一个怪癖是你必须减去一个等于 的数字。 2(位数) - 1 - 1 得到真正的指数;在我们的例子中,这意味着减去 0b1111111111 (十进制数 1023)得到真正的指数, 0b00000000011 (十进制数 3)。

    尾数

    尾数作为 52 位存储在第三个组件中。然而,这个组件也有一个怪癖。要理解这个怪癖,请考虑用科学记数法表示的数字,如下所示:

    6.0221413x1023



    尾数将是 6.0221413 .回想一下,科学记数法中的尾数总是以单个非零数字开头。二进制也是如此,只是二进制只有两位数: 01 .所以二进制尾数总是以 1 开头!存储浮点数时, 1前面的二进制尾数被省略以节省空间;我们必须把它放回第三个元素的前面才能得到真正的尾数:

    1.0010011001100110011001100110011001100110011001100110



    这不仅仅是简单的加法,因为存储在我们的第三个组件中的位实际上代表尾数的小数部分,位于 radix point 的右侧。 .

    在处理十进制数时,我们通过乘以或除以 10 的幂来“移动小数点”。在二进制中,我们可以通过乘以或除以 2 的幂来做同样的事情。由于我们的第三个元素有 52 位,我们除以它来自 252 将其向右移动 52 个位置:

    0.0010011001100110011001100110011001100110011001100110



    在十进制表示法中,这与除 675539944105574 相同来自 4503599627370496获取 0.1499999999999999 . (这是一个可以用二进制精确表示的比率示例,但只能用十进制近似表示;有关详细信息,请参阅: 675539944105574 / 4503599627370496。)

    现在我们已经将第三个分量转换为小数,加上 1给出真正的尾数。

    回顾组件
  • 签名(第一部分):0为阳性,1为负
  • 指数(中间分量):减去 2(位数) - 1 - 1 得到真正的指数
  • 尾数(最后一个分量):除以 2(位数)并添加 1得到真正的尾数


  • 计算数字

    将所有三个部分放在一起,我们得到了这个二进制数:

    1.0010011001100110011001100110011001100110011001100110 x 1011



    然后我们可以将其从二进制转换为十进制:

    1.1499999999999999 x 23 (inexact!)



    并乘以显示我们以 ( 9.2 ) 开头的数字在存储为浮点值后的最终表示:

    9.1999999999999993



    表示为分数

    9.2

    现在我们已经建立了数字,可以将其重建为一个简单的分数:

    1.0010011001100110011001100110011001100110011001100110 x 1011



    将尾数转换为整数:

    10010011001100110011001100110011001100110011001100110 x 1011-110100



    转换为十进制:

    5179139571476070 x 23-52



    减去指数:

    5179139571476070 x 2-49



    将负指数转化为除法:

    5179139571476070 / 249



    乘以指数:

    5179139571476070 / 562949953421312



    这等于:

    9.1999999999999993



    9.5
    >>> float_to_bin_parts(9.5)
    ['0', '10000000010', '0011000000000000000000000000000000000000000000000000']

    您已经可以看到尾数只有 4 位数字后跟一大堆零。但是,让我们逐步了解一下。

    组合二进制科学记数法:

    1.0011 x 1011



    移动小数点:

    10011 x 1011-100



    减去指数:

    10011 x 10-1



    二进制转十进制:

    19 x 2-1



    除法的负指数:

    19 / 21



    乘以指数:

    19 / 2



    等于:

    9.5





    进一步阅读
  • The Floating-Point Guide: What Every Programmer Should Know About Floating-Point Arithmetic, or, Why don’t my numbers add up? (floating-point-gui.de)
  • What Every Computer Scientist Should Know About Floating-Point Arithmetic (戈德堡 1991)
  • IEEE Double-precision floating-point format (维基百科)
  • Floating Point Arithmetic: Issues and Limitations (docs.python.org)
  • Floating Point Binary
  • 关于floating-point - 为什么 float 不准确?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21895756/

    28 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com