Python 为什么用除法而不是乘法来表示精确的二进制数?

Python 为什么用除法而不是乘法来表示精确的二进制数?,python,computer-science,Python,Computer Science,该问题的部分内容已在其他地方(例如)讨论过 下面揭示了通过除法和乘法生成数字的方式的不同: >>> listd = [i/10 for i in range(6)] >>> listm = [i*0.1 for i in range(6)] >>> print(listd) [0.0, 0.1, 0.2, 0.3, 0.4, 0.5] >>> print(listm) [0.0, 0.1, 0.2, 0.3000000000

该问题的部分内容已在其他地方(例如)讨论过

下面揭示了通过除法和乘法生成数字的方式的不同:

>>> listd = [i/10 for i in range(6)]
>>> listm = [i*0.1 for i in range(6)]
>>> print(listd)
[0.0, 0.1, 0.2, 0.3, 0.4, 0.5]
>>> print(listm)
[0.0, 0.1, 0.2, 0.30000000000000004, 0.4, 0.5]
在第二种情况下,0.3的舍入误差约为1e-16,即双浮点精度

但我不了解输出的三个方面:

  • 既然这里唯一可以用二进制表示的数字是0.0和0.5,为什么上面没有打印这些数字呢
  • 为什么这两种列表理解的评价不同
  • 为什么数字的两个字符串表示形式不同,但它们的二进制表示形式不同

  • 这里的关键点是,
    10
    完全用二进制表示,而
    0.1
    则不是。除以10得到每个分数最接近的可能表示形式;乘以0.1的不精确转换不能保证精度。有时你会“接近”到将结果四舍五入到小数点后一位,有时则不然


    这就足够了吗?

    这里的关键点是
    10
    是用二进制表示的,而
    0.1
    不是。除以10得到每个分数最接近的可能表示形式;乘以0.1的不精确转换不能保证精度。有时你会“接近”到将结果四舍五入到小数点后一位,有时则不然


    这就足够了吗?

    回答每个问题:

  • 既然这里唯一可以用二进制表示的数字是0.0和0.5,为什么上面没有打印这些数字呢
  • Python将任何浮点数的显示舍入为最短的文本,该文本在求值时产生相同的值。是的,这些数字中的许多实际上与它们所表示的实际数字并不相同,但是如果您用Python键入它们,您将在没有数学运算的情况下得到(稍微不准确的)值

  • 为什么这两种列表理解的评价不同

  • 如您所述,
    0.1
    已经不准确,因此乘以它并不完全等同于除以
    10
    (其中至少两个输入都是精确整数)。有时,这种不准确意味着结果与除以
    10
    不同;毕竟,你乘以的是“刚刚超过十分之一”,而不是“十分之一”。

    回答每个问题:

  • 既然这里唯一可以用二进制表示的数字是0.0和0.5,为什么上面没有打印这些数字呢
  • Python将任何浮点数的显示舍入为最短的文本,该文本在求值时产生相同的值。是的,这些数字中的许多实际上与它们所表示的实际数字并不相同,但是如果您用Python键入它们,您将在没有数学运算的情况下得到(稍微不准确的)值

  • 为什么这两种列表理解的评价不同

  • 如您所述,
    0.1
    已经不准确,因此乘以它并不完全等同于除以
    10
    (其中至少两个输入都是精确整数)。有时,这种不准确意味着结果与除以
    10
    不同;毕竟,乘以“刚好超过十分之一”,而不是“十分之一”。

    打印浮点数的典型规则是打印出足够的小数位数,这样,如果用户实际键入打印出的内容,他们就可以得到打印出的准确值。0.4不是一个精确的二进制值,但是4/10生成的结果给出了与0.4相同的浮点数,所以这就是需要打印的全部内容。“将结果四舍五入到一个小数位”…这是Python shell自动执行的吗?@mbdeaton:不是shell,但是
    float
    s的
    repr
    可以为您实现这一点。它只能在不影响再现性的情况下尽可能多地循环;键入
    0.100000000000000555
    与键入
    0.1
    获得相同的值,因此它将表示为
    0.1
    。相比之下,
    0.3
    相当于
    0.29999999999889
    ,而不是
    0.300000000000000004
    ,因此,
    0.300000000004
    不能缩短为
    0.3
    。打印浮点数的典型规则是打印足够的小数位,这样,如果用户实际键入打印出的内容,他们就可以得到打印的准确值。0.4不是一个精确的二进制值,但是4/10生成的结果给出了与0.4相同的浮点数,所以这就是需要打印的全部内容。“将结果四舍五入到一个小数位”…这是Python shell自动执行的吗?@mbdeaton:不是shell,但是
    float
    s的
    repr
    可以为您实现这一点。它只能在不影响再现性的情况下尽可能多地循环;键入
    0.100000000000000555
    与键入
    0.1
    获得相同的值,因此它将表示为
    0.1
    。相比之下,
    0.3
    相当于
    0.299999999889
    ,而不是
    0.300000000000000004
    ,因此
    0.300000000000000004
    不能缩短为
    0.3
    >>> def bf(x):
            return bin(struct.unpack('@i',struct.pack('!f',float(x)))[0])
    >>> x1 = 3/10
    >>> x2 = 3*0.1
    >>> print(repr(x1).ljust(20), "=", bf(x1))
    >>> print(repr(x2).ljust(20), "=", bf(x2))
    0.3                  = -0b1100101011001100110011011000010
    0.30000000000000004  = -0b1100101011001100110011011000010