如何可靠地从一个数字中分离小数和浮动部分?

时间:2017-07-19 15:10:49

标签: python floating-point ieee-754

这不是this的重复,我将在此解释。

考虑x = 1.2。我想将其分为10.2。我已经尝试了链接问题中概述的所有这些方法:

In [370]: x = 1.2

In [371]: divmod(x, 1)
Out[371]: (1.0, 0.19999999999999996)

In [372]: math.modf(x)
Out[372]: (0.19999999999999996, 1.0)

In [373]: x - int(x)
Out[373]: 0.19999999999999996

In [374]: x - int(str(x).split('.')[0])
Out[374]: 0.19999999999999996

我尝试的任何内容都没有给我10.2

有没有办法可靠地将浮点数转换为不受浮点表示限制阻碍的十进制和浮点等值?

我理解这可能是由于数字本身的存储方式有限,所以我愿意接受任何克服这一点的建议(如包或其他方式)。

编辑:更喜欢不涉及字符串操作的方式,如果可能

4 个答案:

答案 0 :(得分:5)

解决方案

它可能看起来像一个黑客,但你可以分离字符串形式(实际上是repr)并将其转换回整数和浮点数:

In [1]: x = 1.2

In [2]: s = repr(x)

In [3]: p, q = s.split('.')

In [4]: int(p)
Out[4]: 1

In [5]: float('.' + q)
Out[5]: 0.2

如何运作

以这种方式接近它的原因是显示1.2的{​​{3}}非常复杂(internal algorithm的快速变体)。它很难显示无法准确表示的数字的最短可能表示。通过拆分 repr 表单,您可以利用该算法。

在内部,输入为1.2的值存储为二进制分数5404319552844595 / 4503599627370496,实际上等于1.1999999999999999555910790149937383830547332763671875。 Gay算法用于将其显示为字符串1.2 split 然后可靠地提取整数部分。

In [6]: from decimal import Decimal

In [7]: Decimal(1.2)
Out[7]: Decimal('1.1999999999999999555910790149937383830547332763671875')

In [8]: (1.2).as_integer_ratio()
Out[8]: (5404319552844595, 4503599627370496)

理由和问题分析

如上所述,您的问题大致转换为"我希望将数字的整数部分和小数部分分开,因为它在视觉上看起来而不是根据它实际存储的方式"。

以这种方式框架,很明显解决方案涉及解析它如何在视觉上显示。虽然它让人感觉像是黑客,但这是利用非常复杂的显示算法并实际匹配你看到的最直接的方式。

除非您手动重现内部显示算法,否则这种方式可能是唯一可靠匹配您所看到的方式。

替代方案失败

如果你想保持整数域,你可以尝试舍入和减法,但这会给你一个浮点部分的意外值:

In [9]: round(x)
Out[9]: 1.0

In [10]: x - round(x)
Out[10]: 0.19999999999999996

答案 1 :(得分:1)

这是一个没有字符串操作的解决方案(frac_digits是十进制数字的计数,您可以保证数字的小数部分适合):

>>> def integer_and_fraction(x, frac_digits=3):
...     i = int(x)
...     c = 10**frac_digits
...     f = round(x*c-i*c)/c
...     return (i, f)
... 
>>> integer_and_fraction(1.2)
(1, 0.2)
>>> integer_and_fraction(1.2, 1)
(1, 0.2)
>>> integer_and_fraction(1.2, 2)
(1, 0.2)
>>> integer_and_fraction(1.2, 5)
(1, 0.2)
>>> 

答案 2 :(得分:0)

您可以尝试将1.2转换为字符串,然后拆分'。'然后将两个字符串(" 1"和" 2")转换回您想要的格式。

另外用“' 0”填充第二部分。会给你一个很好的格式。

答案 3 :(得分:0)

所以我只是在python终端中做了以下操作,它似乎正常工作......

x=1.2
s=str(x).split('.')
i=int(s[0])
d=int(s[1])/10