【问题标题】:When does floating point round exactly?浮点数何时精确取整?
【发布时间】:2017-03-15 20:31:23
【问题描述】:

我的问题被标记为与https://stackoverflow.com/a/22023918/856090 重复,但它不是重复的。该问题涵盖了不精确表示的情况,而我的问题是关于特殊情况下的精确表示

在 Python 3 会话中:

>>> 0.1*3/3
0.10000000000000002
>>> 12.34567
12.34567

第一次计算产生的结果不准确。 (我明白为什么:这是因为浮点数是四舍五入的。)

但第二次计算(从十进制分数切换到二进制 FP,然后再返回)会产生精确的结果。

我的问题是为什么第二个结果(以及许多类似的“计算”,只涉及一个没有算术运算的 FP 数)是准确的(与在 Python 会话中键入的相同)?

另外:两个小数(其指数相差不大)的和的精确性如何?

【问题讨论】:

标签: floating-point precision floating-accuracy


【解决方案1】:

这不是“精确的”... 12.34567 没有二进制64 的精确表示。但是 Python 的浮点到字符串转换的默认规则会产生与您输入的输入相同的结果。如果您强制它为您提供整个故事,它会暴露出不精确性:

>>> '{:.500}'.format(12.34567)
'12.3456700000000001438138497178442776203155517578125'

【讨论】:

    【解决方案2】:

    精确表示有两个限制。

    第一个消除了诸如 12.34567 和 0.1 之类的数字,它必须具有作为基数 2 分数的有限表示。它必须等于 A/B,其中 A 是整数,B 是 2 的整数幂,例如 1、2、4、8 等。

    这可以很容易地测试。首先将数字写为比率。例如,0.1 是 1/10。减少到最低条件。 1 和 10 除了 1 之外没有共同的整数因子,所以 1/10 已经减少了。看分母,10。它不是 2 的幂,所以 0.1 不能完全表示。

    现在考虑 0.375。它等于 375/1000 或 3/8。 8 是 2 的幂,因此 0.375 具有有限二进制小数表示。

    第二组条件限制所涉及的数字的大小以适应特定的浮点格式。

    【讨论】:

    • 这回答了标题,但不是这个:“我的问题是为什么第二个结果是准确的(与在 Python 会话中键入的相同)?”
    • @JohnKugelman 无法解释为什么第二个结果是准确的,因为它不是。正如前面的答案所指出的,默认的浮点到字符串转换精度隐藏了不精确性。
    • 这应该在你的答案中。
    • @JohnKugelman 为什么?已经处理了任意示例是准确的错误假设。我要么就如何确定准确性的更广泛的问题发帖,要么根本不发帖。仅仅重复前面的答案是没有意义的。
    【解决方案3】:

    你不能做以 10 为基础的事情并假设它们以相同的方式在以 2 为基础工作。

    双精度 IEEE

    0.1 = 0x3FB999999999999A
    3.0 = 0x4008000000000000
    12.34567 = 0x4028B0FBA8826AA9
    0.1*3.0 = 0x3FD3333333333334
    (0.1*3.0)/3.0 = 0x3FB999999999999B
    

    单在计算器上更容易处理,并注意有趣的区别:

    0.1 = 0x3DCCCCCD
    3.0 = 0x40400000
    12.34567 = 0x414587DD
    0.1*3.0 = 0x3E99999A
    (0.1*3.0)/3.0 = 0x3DCCCCCD
    

    所以首先

    0x3DCCCCCD is 0 01111011 10011001100110011001101
    

    如果分母 1/3 = 0.3333333 中有一个 3,请注意与以十为底的重复模式。就像 1/6 0.16666667 因为我们四舍五入。好吧,看看最后一点,它最后会是 1001,但这有点太多了,如果你要砍掉的点之后的数字大于你四舍五入的一半,那么二进制中的 0.1001 舍入到 0.101是的?同样的交易。

    在这种情况下,对于您的十进制数字,单精度得出正确答案,将某个数字乘以 3,然后再除以得到相同的数字,但在不同点的舍入剪辑加倍时,我们再次四舍五入并最终得到的数字比开始时要大得多。

    这一切都可以很容易地以十进制形式显示。如果我的格式是固定位数,我必须在某个点停止,所以可以说 1/6 = 0.166666 或者它是 0.166667。和 6.0 可以说是 6.00000。因此,如果我使用十进制浮点格式并乘以 6 * (1/6),我得到 0.99996 或 1.000002 在这两种情况下都不会得到 1。不是因为四舍五入,而是因为数字无法以该格式准确表示。

    如果您从某个以 10 为基数的 ASCII 字符串变为浮点数,然后又回到某个以 10 为基数的 ASCII 字符串时,如果您恢复了开始时的数字,那么这只是运气。该数字要么以浮点数准确表示,要么没有。您可以通过上面非常简单的以 10 为底的浮点数看到,无论您有多少位接近无穷大,您都不会得到 6 * (1/6) = 1,因为以 10 为底的浮点数不能准确地表示 1/6固定位数。

    查看你的其他号码

    12.34567 = 0x414587DD
    0 10000010 10001011000011111011101
    12.34567 = 0x4028B0FBA8826AA9
    0 10000000010 1000101100001111101110101000100000100110101010101001
    
    1.10001011000011111011101
    1.1000101100001111101110101000100000100110101010101001
    

    很明显 single 没有足够的位来表示它,并且没有向上舍入,因为下一位是小于一半的零。并且双打一直工作到最后,我不会将它转换回以 10 为底的数字,但我愿意打赌你只是走运了。如果你有更多的比特,那会安定下来吗?同样,我不会为您手动转换。我认为您应该能够看到,即使您只是查看 0.1 并转到浮动并返回 ASCII,您也不会得到 0.1。就像以十进制为基础的 1/6 不会在那里和回来转换,如果你将它四舍五入,你会很幸运:5.9999... 如果你在正确的位置剪辑/四舍五入,则四舍五入为 6.0,但这只是 ASCII 转换的四舍五入而不是浮点格式表示的实际数字。

    【讨论】:

    • 是的,这个话题已经被讨论过无数次了,链接的问题很可能是重复的......或者这是那些重复的问题。
    • 浮点数何时精确取整?在尾数的最后一个数字和尾数被剪掉之前的粘性位之间,就像小学的 10 进制一样。四舍五入到 N 位数。位数有据可查,维基百科有很好的图片。
    猜你喜欢
    • 1970-01-01
    • 2010-12-26
    • 1970-01-01
    • 1970-01-01
    • 2012-11-05
    • 1970-01-01
    • 2015-11-12
    • 1970-01-01
    • 2016-06-01
    相关资源
    最近更新 更多