Python中str(x)的默认行为

3 投票
2 回答
4576 浏览
提问于 2025-04-17 17:04

我在用一些代码,这些代码使用了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 个回答

3

这里有几个值得讨论的问题,但总结一下就是:你无法提取那些已经不在你系统上的信息

如果你把一个十进制数字存储为浮点数,那么你就会丢失一些信息,因为大多数有限位数的十进制数字无法用有限位数的二进制(也就是计算机用的方式)来存储。

正如之前提到的,str(a_float) 实际上会调用 a_float.__str__()根据文档的说明,这个方法的目的是

返回一个包含对象可打印表示的字符串

对于 float 类型,并没有特别的定义。我的看法是,对于你的需求来说,应该把 __str__ 的行为视为未定义,因为没有官方的文档说明它 - 当前的实现随时可能会改变。

如果你没有原始的字符串,就无法从 float 对象中提取出缺失的十进制数字。你能做的就是进行可预测的四舍五入,使用字符串格式化(你提到的):

Decimal( "{0:.5f}".format(a_float) )

你还可以用 resulting_string.rstrip("0") 去掉右边的0。不过,再次强调,这个方法并不能恢复已经丢失的信息。

4

在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是如何转换浮点数的。

撰写回答