今天我遇到了同样的问题/疑问,但迄今为止我对给出的任何答案都不完全满意。问题的核心似乎是:
有人能建议一种好的方法将浮点数转换为十进制数[...],可能限制可支持的有效数字位数吗?
简短的答案/解决方案:可以。
def ftod(val, prec = 15):
return Decimal(val).quantize(Decimal(10)**-prec)
长答案:
如nosklo所指出的那样,将用户输入转换为浮点数后无法保留其输入。
但是可以使用合理的精度对该值进行四舍五入并将其转换为Decimal。
在我的情况下,我只需要小数点后2到4位数字,但它们需要准确。让我们考虑经典的0.1 + 0.2 == 0.3检查。
>>> 0.1 + 0.2 == 0.3
False
现在让我们通过转换为十进制来完成这个例子(完整示例):
>>> from decimal import Decimal
>>> def ftod(val, prec = 15): # float to Decimal
... return Decimal(val).quantize(Decimal(10)**-prec)
...
>>> ftod(0.1) + ftod(0.2) == ftod(0.3)
True
Ryabchenko Alexander的回答对我非常有帮助。只是缺少一种动态设置精度的方法 - 这是我想要(也许也需要)的功能。 Decimal文档FAQ提供了一个示例,说明如何构造quantize()所需的参数字符串:
>>> Decimal(10)**-4
Decimal('0.0001')
以下是18位数字分隔符后的打印效果(来自于C语言编程,我喜欢Python中的花式表达):
>>> for x in [0.1, 0.2, 0.3, ftod(0.1), ftod(0.2), ftod(0.3)]:
... print("{:8} {:.18f}".format(type(x).__name__+":", x))
...
float: 0.100000000000000006
float: 0.200000000000000011
float: 0.299999999999999989
Decimal: 0.100000000000000000
Decimal: 0.200000000000000000
Decimal: 0.300000000000000000
最后,我想知道比较仍然有效的精度是多少:
>>> for p in [15, 16, 17]:
... print("Rounding precision: {}. Check 0.1 + 0.2 == 0.3 is {}".format(p,
... ftod(0.1, p) + ftod(0.2, p) == ftod(0.3, p)))
...
Rounding precision: 15. Check 0.1 + 0.2 == 0.3 is True
Rounding precision: 16. Check 0.1 + 0.2 == 0.3 is True
Rounding precision: 17. Check 0.1 + 0.2 == 0.3 is False
对于最大精度,15似乎是很好的默认值。这在大多数系统上都可以正常工作。如果您需要更多信息,请尝试:
>>> import sys
>>> sys.float_info
sys.float_info(max=1.7976931348623157e+308, max_exp=1024, max_10_exp=308, min=2.2250738585072014e-308, min_exp=-1021, min_10_exp=-307, dig=15, mant_dig=53, epsilon=2.220446049250313e-16, radix=2, rounds=1)
在我的系统上,float类型的尾数有53位,我计算了它所表示的十进制数字的位数:
>>> import math
>>> math.log10(2**53)
15.954589770191003
这告诉我我们用53位可以获得近16位数字。因此,15位对于精度值来说是足够的,并且应该始终有效。16位容易出错,17位肯定会引起麻烦(如上所述)。
无论如何,在我的特定情况下,我只需要2到4位小数精度,但作为一个完美主义者,我很享受调查这个问题 :-)
欢迎任何建议/改进/投诉。