Python浮动到十进制转换

Koz*_*huk 52 python decimal

Python Decimal不支持从float构造; 它希望你必须先将float转换为字符串.

这非常不方便,因为浮点数的标准字符串格式化程序要求您指定小数位数而不是重要位置.因此,如果你有一个数字,可能有多达15个小数位,你需要格式化为十进制(""%.15f"%my_float),如果你之前也有任何有效数字,这将给你15位小数的垃圾小数.

有人建议一个很好的方法从用户输入的浮点数转换为十进制保留值,可能会限制可支持的有效位数吗?

jfs*_*jfs 55

Python <2.7

"%.15g" % f
Run Code Online (Sandbox Code Playgroud)

或者在Python 3.0中:

format(f, ".15g")
Run Code Online (Sandbox Code Playgroud)

Python 2.7 +,3.2 +

只需将float Decimal直接传递给构造函数,如下所示:

from decimal import Decimal
Decimal(f)
Run Code Online (Sandbox Code Playgroud)

  • 不要忘记包括:从十进制导入十进制并使用d =十进制转换(f) (8认同)
  • 将 float 直接传递给“Decimal”构造函数会引入舍入错误。最好在将浮点数传递给构造函数之前将其转换为字符串。例如 `Decimal(0.30000000000000004)` 结果为 `Decimal('0.3000000000000000444089209850062616169452667236328125')` 和 `Decimal(str(0.300000000000000004))`结果为“十进制('0.30000000000000004')”。 (8认同)
  • @AliaksandrAdzinets:需要明确的是:这不是舍入错误。浮点数 `0.30000000000000004` 和 `'0.30000000000000004'` 表示的数字是不同的(因为后者不能完全表示为浮点数),即 Decimal 不会在这里引入错误,它与 `str()` 相反改变 float 的值:恰好 `str()` 产生的错误对于 float 的简单视图来说可能是理想的 (5认同)
  • @AliaksandrAdzinets:这取决于您想要实现的目标。获取与 [float](https://en.wikipedia.org/wiki/Double- precision_floating-point_format#IEEE_754_double- precision_binary_floating-point_format:_binary64) (`0.30000000000000004 .as_integer_ratio()` -- 内部表示相对应的值可以认为是用科学计数法写的二进制数),直接传递float。要获得(天真的)“所见即所得”小数比: `30000000000000004/100000000000000000` ,请传递 string. https://docs.python.org/3/tutorial/floatingpoint.html (2认同)

nos*_*klo 24

你在问题中说:

当用户输入时,有人可以建议一种从浮点数转换为十进制保留值的好方法

但每次用户输入值时,都会以字符串形式输入,而不是以浮点形式输入.你正在把它转换成某个浮点数.直接将其转换为十进制,不会丢失任何精度.

  • 然后你已经失去了"用户输入的价值",你无法取回它.你所能做的只是应用任意四舍五入和希望.Python对Decimal的处理正确地引起了你的注意,所以你现在就能理解这个问题而不是后来得到一个奇怪的bug. (10认同)
  • @Kozyarchuk:我知道您说过您不能更改架构,但是您可能要考虑将其更改为十进制类型(在某些时候),因为将来这会变得更加令人头疼。 (2认同)

小智 13

我建议这个

>>> a = 2.111111
>>> a
2.1111110000000002
>>> str(a)
'2.111111'
>>> decimal.Decimal(str(a))
Decimal('2.111111')
Run Code Online (Sandbox Code Playgroud)

  • 这种方法给出了最明智的结果。按照Sebastian的答案直接在浮点数附近构造一个Decimal可以在Python 2.7中产生令人惊讶的结果。好吧,如果您以前玩过花车,一点也不奇怪... (2认同)
  • 与此相关的一个注释(如使用 `repr()` 的答案的评论中提出的)是,这里的 `str()` 将限制为 17 位小数,例如 `Decimal(str(0.12345678901234567890))` 结果为 `Decimal('0.12345678901234568 ')`。 (2认同)

Rya*_*der 8

您可以转换并量化以在逗号后保留 5 位数字

Decimal(1.89977787898).quantize(Decimal("1.00000"))
Run Code Online (Sandbox Code Playgroud)


Chr*_*phK 6

我今天遇到了同样的问题,我对迄今为止给出的任何答案都不完全满意。问题的核心似乎是:

\n
\n

有人可以建议一种从浮点转换为十进制的好方法[...]也许限制可以支持的有效数字的数量?

\n
\n

简短的回答/解决方案:是的。

\n
def ftod(val, prec = 15):\n    return Decimal(val).quantize(Decimal(10)**-prec)\n
Run Code Online (Sandbox Code Playgroud)\n

长答案:

\n

正如 nosklo 指出的那样,在转换为浮点数后不可能保留用户的输入。\n但可以以合理的精度舍入该值并将其转换为十进制。

\n

就我而言,分隔符后只需要 2 到 4 位数字,但它们需要准确。让我们考虑一下经典的 0.1 + 0.2 == 0.3 检查。

\n
>>> 0.1 + 0.2 == 0.3\nFalse\n
Run Code Online (Sandbox Code Playgroud)\n

现在让我们通过转换为十进制来完成此操作(完整示例):

\n
>>> from decimal import Decimal\n>>> def ftod(val, prec = 15):   # float to Decimal\n...     return Decimal(val).quantize(Decimal(10)**-prec)\n... \n>>> ftod(0.1) + ftod(0.2) == ftod(0.3)\nTrue\n
Run Code Online (Sandbox Code Playgroud)\n

Ryabchenko Alexander 的回答对我来说非常有帮助。它只是缺少一种动态设置精度 \xe2\x80\x93 的方法,这是我想要的(也许也需要)功能。Decimal文档常见问题解答提供了有关如何构造 quantize() 所需参数字符串的示例:

\n
>>> Decimal(10)**-4\nDecimal(\'0.0001\')\n
Run Code Online (Sandbox Code Playgroud)\n

下面是在分隔符后用 18 位数字打印的数字的样子(来自 C 编程,我喜欢花哨的 Python 表达式):

\n
>>> for x in [0.1, 0.2, 0.3, ftod(0.1), ftod(0.2), ftod(0.3)]:\n...     print("{:8} {:.18f}".format(type(x).__name__+":", x))\n... \nfloat:   0.100000000000000006\nfloat:   0.200000000000000011\nfloat:   0.299999999999999989\nDecimal: 0.100000000000000000\nDecimal: 0.200000000000000000\nDecimal: 0.300000000000000000\n
Run Code Online (Sandbox Code Playgroud)\n

最后我想知道比较的精度仍然有效:

\n
>>> for p in [15, 16, 17]:\n...     print("Rounding precision: {}. Check  0.1 + 0.2 == 0.3  is {}".format(p,\n...         ftod(0.1, p) + ftod(0.2, p) == ftod(0.3, p)))\n... \nRounding precision: 15. Check  0.1 + 0.2 == 0.3  is True\nRounding precision: 16. Check  0.1 + 0.2 == 0.3  is True\nRounding precision: 17. Check  0.1 + 0.2 == 0.3  is False\n
Run Code Online (Sandbox Code Playgroud)\n

15 似乎是最大精度的默认值。这应该适用于大多数系统。如果您需要更多信息,请尝试:

\n
>>> import sys\n>>> sys.float_info\nsys.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)\n
Run Code Online (Sandbox Code Playgroud)\n

在我的系统上,浮点数有 53 位尾数,我计算了小数位数:

\n
>>> import math\n>>> math.log10(2**53)\n15.954589770191003\n
Run Code Online (Sandbox Code Playgroud)\n

这告诉我,使用 53 位,我们几乎可以得到16 位数字。因此 15 对于精度值来说是合适的并且应该始终有效。16 容易出错,而 17 肯定会引起麻烦(如上所示)。

\n

不管怎样......在我的具体情况下,我只需要 2 到 4 位的精度,但作为一个完美主义者,我喜欢研究这个:-)

\n

欢迎任何建议/改进/投诉。

\n


Fed*_*oni 5

浮点数的“官方”字符串表示形式由内置的 repr() 给出:

>>> repr(1.5)
'1.5'
>>> repr(12345.678901234567890123456789)
'12345.678901234567'
Run Code Online (Sandbox Code Playgroud)

您可以使用 repr() 而不是格式化字符串,结果不会包含任何不必要的垃圾。

  • @Kozyarchuk:这不是有效数字位数的问题。这是十进制数的二进制表示的问题。你的例子在 0.6 上也失败了。 (4认同)

muh*_*huk 5

Python 支持从浮点数创建小数。您只需先将其转换为字符串即可。但字符串转换不会发生精度损失。您要转换的浮点数一开始就不具有这种精度。(否则你就不需要十进制)

我认为这里的困惑在于我们可以以十进制格式创建浮点文字,但是一旦解释器消耗该文字,内部表示就会变成浮点数。