关于python:如何从数字中可靠地分离小数和浮动部分?

How to reliably separate decimal and floating parts from a number?

这不是这个的副本,我在这里解释。

x = 1.2为例。我想把它分为10.2。我已经尝试了所有这些方法,如链接问题中所概述的:

1
2
3
4
5
6
7
8
9
10
11
12
13
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

我所做的一切都没有给我确切的答案。

是否有任何方法可以可靠地将浮点数转换为十进制和浮点数等价物,而不受浮点数表示的限制?

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

编辑:如果可能的话,最好使用不涉及字符串操作的方法。


解决方案

它看起来像一个黑客,但你可以分离字符串形式(实际RPR),并将其转换为int和浮动:

1
2
3
4
5
6
7
8
9
10
11
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的内部算法非常复杂(大卫·盖伊算法的快速变体)。它很难显示出无法精确表示的数字的最短表示法。通过拆分repr表单,您可以利用该算法。

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

1
2
3
4
5
6
7
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)

基本原理和问题分析

如前所述,您的问题大致翻译为"我希望根据数字的实际存储方式,将数字的整数部分和小数部分进行拆分,因为它在视觉上看起来更像是这样"。

以这种方式构建,很明显,解决方案涉及解析它的可视化显示方式。虽然这让人感觉像是一次黑客攻击,但这是利用非常复杂的显示算法并与您所看到的内容相匹配的最直接的方法。

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

备选方案失效

如果您想保持整数的状态,您可以尝试舍入和减法,但这会为浮点部分提供一个意外的值:

1
2
3
4
5
In [9]: round(x)
Out[9]: 1.0

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


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
>>> 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)
>>>


所以我在一个python终端上做了以下的工作,它看起来工作正常…

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


您可以尝试将1.2转换为字符串,在"."上拆分,然后将两个字符串("1"和"2")转换回所需的格式。

另外,用"0"填充第二部分。这将为您提供一个很好的格式。