Python中str(x)的默认行为
我在用一些代码,这些代码使用了Decimal类,因为它需要精确到特定的小数位数。有些函数允许输入浮点数,这是因为它与代码的其他部分有交互。为了把这些浮点数转换成Decimal对象,它使用了类似这样的方式:
mydec = decimal.Decimal(str(x))
这里的x就是输入的浮点数。我的问题是,有人知道浮点数的'str'方法的标准是什么吗?
举个例子,数字2.1234512在内部存储时是2.12345119999999999,这是因为浮点数的表示方式。
>>> x = 2.12345119999999999
>>> x
2.1234511999999999
>>> str(x)
'2.1234512'
好的,在这种情况下,str(x)的作用类似于'%.6f' % x。这就导致了我代码在转换为Decimal时出现问题。看看下面这个:
>>> d = decimal.Decimal('2.12345119999999999')
>>> ds = decimal.Decimal(str(2.12345119999999999))
>>> d - ds
Decimal('-1E-17')
如果我有浮点数2.12345119999999999,想把它传给Decimal,使用str()转换成字符串会得到错误的结果。我需要知道str(x)的规则是什么,这些规则决定了格式是什么,因为我需要判断这段代码是否需要重写,以避免这个错误(注意,可能是可以的,因为例如,代码可能在得到Decimal对象后会四舍五入到第十位小数)
在python的文档中肯定有一套规则,希望这里有人能指点我一下。谢谢!
2 个回答
这里有几个值得讨论的问题,但总结一下就是:你无法提取那些已经不在你系统上的信息。
如果你把一个十进制数字存储为浮点数,那么你就会丢失一些信息,因为大多数有限位数的十进制数字无法用有限位数的二进制(也就是计算机用的方式)来存储。
正如之前提到的,str(a_float)
实际上会调用 a_float.__str__()
。根据文档的说明,这个方法的目的是
返回一个包含对象可打印表示的字符串
对于 float
类型,并没有特别的定义。我的看法是,对于你的需求来说,应该把 __str__
的行为视为未定义,因为没有官方的文档说明它 - 当前的实现随时可能会改变。
如果你没有原始的字符串,就无法从 float
对象中提取出缺失的十进制数字。你能做的就是进行可预测的四舍五入,使用字符串格式化(你提到的):
Decimal( "{0:.5f}".format(a_float) )
你还可以用 resulting_string.rstrip("0")
去掉右边的0。不过,再次强调,这个方法并不能恢复已经丢失的信息。
在Python的源代码中,可以找到“Include/floatobject.h”这个文件。字符串转换的精度设置在文件顶部几行的地方,那里有一段注释解释了这个选择的原因:
/* The str() precision PyFloat_STR_PRECISION is chosen so that in most cases,
the rounding noise created by various operations is suppressed, while
giving plenty of precision for practical use. */
#define PyFloat_STR_PRECISION 12
如果你需要不同的设置,可以选择重新编译代码。任何修改都会影响浮点数和复数的格式。你可以查看./Objects/complexobject.c和./Objects/floatobject.c这两个文件。此外,你还可以比较这两个文件中repr和str是如何转换浮点数的。