并不只是Python实现的问题,适用于任何将浮点数转为十进制字符串的函数。
浮点数本质上是一个二进制数,但采用科学计数法表示,并具有固定数量的有效数字。
任何具有质数因子且不与底数共享的数字的倒数总会产生一个循环小数。例如,1/7具有质数因子7,它不与10共享,因此具有循环小数表示形式;同样的,1/10具有质数因子2和5,后者不与2共享。这意味着0.1无法通过有限数量的二进制位来精确表示。
由于0.1没有精确表示,将其近似为十进制字符串的函数通常会尝试近似某些值,以避免出现像0.1000000000004121这样的不可理解的结果。
由于浮点数采用科学计数法表示,因此乘以基数的幂仅会影响数字的指数部分。例如,在十进制表示中,1.231e+2 * 100 = 1.231e+4,在二进制表示中,同样地,1.00101010e11 * 100 = 1.00101010e101。如果乘以非基数的幂,则有效数字也会受到影响。例如,1.2e1 * 3 = 3.6e1。
根据所使用的算法,可能会尝试根据有效数字猜测常见的小数。0.1和0.4在二进制中具有相同的有效数字,因为它们的浮点数本质上是(8/5)×(2^-4)和(8/5)×(2^-6)的截断。如果算法将8/5的有效数字模式识别为十进制数1.6,则它将适用于0.1、0.2、0.4、0.8等。它还可以针对其他组合具有神奇的有效数字模式,例如除以10得到的float 3和float 10的浮点数模式。
在3*0.1的情况下,最后几个有效数字可能与通过float 3除以float 10得到的0.3常数不同,这取决于它对精度损失的容忍程度,从而导致算法无法识别0.3常数的神奇数字。
编辑:
https://docs.python.org/3.1/tutorial/floatingpoint.html
有趣的是,许多不同的十进制数可以共用一个最近似的二进制分数。例如,数字0.1、0.10000000000000001和0.1000000000000000055511151231257827021181583404541015625都可以近似表示为3602879701896397 / 2 ** 55。由于所有这些十进制值都共用同一近似值,因此任何一个数都可以被显示,同时仍保持invariant eval(repr(x)) == x。
精度损失是无法容忍的,如果浮点数x(0.3)不等于浮点数y(0.1*3),那么repr(x)就不完全等于repr(y)。
0.3000000000000000444089209850062616169452667236328125
显示为0.30000000000000004
,将0.40000000000000002220446049250313080847263336181640625
显示为.4
,尽管它们似乎具有相同的精度,因此没有回答这个问题。 - Mooing Duck