为什么 4*0.1 的浮点值在 Python 3 中看起来不错,但 3*0.1 不好看? [英] Why does the floating-point value of 4*0.1 look nice in Python 3 but 3*0.1 doesn't?

查看:16
本文介绍了为什么 4*0.1 的浮点值在 Python 3 中看起来不错,但 3*0.1 不好看?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我知道大多数小数都没有精确的浮点表示(浮点数学有问题吗?).p>

但我不明白为什么 4*0.1 可以很好地打印为 0.4,但 3*0.1 不是,当这两个值实际上都有丑陋的十进制表示:

>>>3*0.10.30000000000000004>>>4*0.10.4>>>从十进制导入十进制>>>十进制(3*0.1)十进制('0.30000000000000000444089209850062616169452667236328125')>>>十进制(4*0.1)十进制('0.400000000000000002220446049250313080847263336181640625')

解决方案

简单的答案是因为 3*0.1 != 0.3 由于量化(舍入)误差(而 4*0.1== 0.4 因为乘以 2 的幂通常是精确"运算).Python 试图找到可以四舍五入到所需值的最短字符串,因此它可以将 4*0.1 显示为 0.4,因为它们是相等的,但它不能将 3*0.1 显示为 0.3 因为它们不相等.

您可以使用 Python 中的 .hex 方法查看数字的内部表示(基本上是 exact 二进制浮点值,而不是 base-10近似).这有助于解释幕后发生的事情.

>>>(0.1).hex()'0x1.999999999999ap-4'>>>(0.3).hex()'0x1.3333333333333p-2'>>>(0.1*3).hex()'0x1.3333333333334p-2'>>>(0.4).hex()'0x1.999999999999ap-2'>>>(0.1*4).hex()'0x1.999999999999ap-2'

0.1 是 0x1.999999999999a 乘以 2^-4.一个"最后表示数字 10 - 换句话说,二进制浮点中的 0.1 非常轻微 比精确"大.值 0.1(因为最终的 0x0.99 向上舍入为 0x0.a).当您将此乘以 4(2 的幂)时,指数会向上移动(从 2^-4 到 2^-2),但数字不会改变,因此 4*0.1 == 0.4.

但是,当您乘以 3 时,0x0.99 和 0x0.a0 (0x0.07) 之间的微小差异会放大为 0x0.15 错误,在最后一个位置显示为一位数错误.这会导致 0.1*3 比 0.3 的舍入值非常轻微大.

Python 3 的浮点 repr 被设计为 round-trippable,也就是说,显示的值应该完全可以转换为原始值(float(repr(f)) == f 对于所有浮点数 f).因此,它不能以完全相同的方式显示0.30.1*3,否则两个不同的数字会在往返后最终相同.因此,Python 3 的 repr 引擎选择显示一个带有轻微明显错误的文件.

I know that most decimals don't have an exact floating point representation (Is floating point math broken?).

But I don't see why 4*0.1 is printed nicely as 0.4, but 3*0.1 isn't, when both values actually have ugly decimal representations:

>>> 3*0.1
0.30000000000000004
>>> 4*0.1
0.4
>>> from decimal import Decimal
>>> Decimal(3*0.1)
Decimal('0.3000000000000000444089209850062616169452667236328125')
>>> Decimal(4*0.1)
Decimal('0.40000000000000002220446049250313080847263336181640625')

解决方案

The simple answer is because 3*0.1 != 0.3 due to quantization (roundoff) error (whereas 4*0.1 == 0.4 because multiplying by a power of two is usually an "exact" operation). Python tries to find the shortest string that would round to the desired value, so it can display 4*0.1 as 0.4 as these are equal, but it cannot display 3*0.1 as 0.3 because these are not equal.

You can use the .hex method in Python to view the internal representation of a number (basically, the exact binary floating point value, rather than the base-10 approximation). This can help to explain what's going on under the hood.

>>> (0.1).hex()
'0x1.999999999999ap-4'
>>> (0.3).hex()
'0x1.3333333333333p-2'
>>> (0.1*3).hex()
'0x1.3333333333334p-2'
>>> (0.4).hex()
'0x1.999999999999ap-2'
>>> (0.1*4).hex()
'0x1.999999999999ap-2'

0.1 is 0x1.999999999999a times 2^-4. The "a" at the end means the digit 10 - in other words, 0.1 in binary floating point is very slightly larger than the "exact" value of 0.1 (because the final 0x0.99 is rounded up to 0x0.a). When you multiply this by 4, a power of two, the exponent shifts up (from 2^-4 to 2^-2) but the number is otherwise unchanged, so 4*0.1 == 0.4.

However, when you multiply by 3, the tiny little difference between 0x0.99 and 0x0.a0 (0x0.07) magnifies into a 0x0.15 error, which shows up as a one-digit error in the last position. This causes 0.1*3 to be very slightly larger than the rounded value of 0.3.

Python 3's float repr is designed to be round-trippable, that is, the value shown should be exactly convertible into the original value (float(repr(f)) == f for all floats f). Therefore, it cannot display 0.3 and 0.1*3 exactly the same way, or the two different numbers would end up the same after round-tripping. Consequently, Python 3's repr engine chooses to display one with a slight apparent error.

这篇关于为什么 4*0.1 的浮点值在 Python 3 中看起来不错,但 3*0.1 不好看?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆