Splitting a number into the integer and decimal parts

Is there a pythonic way of splitting a number such as 1234.5678 into two parts (1234, 0.5678) i.e. the integer part and the decimal part?

190042 次浏览
>>> a = 147.234
>>> a % 1
0.23400000000000887
>>> a // 1
147.0
>>>

如果希望将整数部分作为整数而不是浮点数,则使用 int(a//1)代替。在一个段落中获得元组: (int(a//1), a%1)

编辑: 记住这个 浮点数的小数部分是近似的,所以如果你想像人类那样表示它,你需要使用 十进制图书馆

intpart,decimalpart = int(value),value-int(value)

对正数有效。

使用 math.modf:

import math
x = 1234.5678
math.modf(x) # (0.5678000000000338, 1234.0)

我们可以使用一个不出名的内置函数; divmod:

>>> s = 1234.5678
>>> i, d = divmod(s, 1)
>>> i
1234.0
>>> d
0.5678000000000338

我是这么做的:

num = 123.456
split_num = str(num).split('.')
int_part = int(split_num[0])
decimal_part = int(split_num[1])

这种变体允许获得所需的精度:

>>> a = 1234.5678
>>> (lambda x, y: (int(x), int(x*y) % y/y))(a, 1e0)
(1234, 0.0)
>>> (lambda x, y: (int(x), int(x*y) % y/y))(a, 1e1)
(1234, 0.5)
>>> (lambda x, y: (int(x), int(x*y) % y/y))(a, 1e15)
(1234, 0.5678)

如果你不介意使用 NumPy,那么:

In [319]: real = np.array([1234.5678])


In [327]: integ, deci = int(np.floor(real)), np.asscalar(real % 1)


In [328]: integ, deci
Out[328]: (1234, 0.5678000000000338)

I have come up with two statements that can divide positive and negative numbers into integer and fraction without compromising accuracy (bit overflow) and speed.

例如,值 100.1323的正值或负值将被分为:
100.1323-> (1000.1323)
-100.1323-> (-100-0.1323)

密码

# Divide a number (x) into integer and fraction
i = int(x) # Get integer
f = (x*1e17 - i*1e17) / 1e17 # Get fraction

极速测试

The performance test shows that the two statements are faster than math.modf, as long as they are not put into their own function or method.

返回文章页面

#!/usr/bin/env python
import math
import cProfile


""" Get the performance of both statements and math.modf """


X = -100.1323  # The number to be divided into integer and fraction
LOOPS = range(5 * 10 ** 6)  # Number of loops




def scenario_a():
""" Get the performance of the statements """
for _ in LOOPS:
i = int(X)  # -100
f = (X*1e17-i*1e17)/1e17  # -0.1323




def scenario_b():
""" Tests the speed of the statements when integer need to be float.
NOTE: The only difference between this and math.modf is the accuracy """
for _ in LOOPS:
i = int(X)  # -100
i, f = float(i), (X*1e17-i*1e17)/1e17  # (-100.0, -0.1323)




def scenario_c():
""" Tests the speed of the statements in a function """
def modf(x):
i = int(x)
return i, (x*1e17-i*1e17)/1e17


for _ in LOOPS:
i, f = modf(X)  # (-100, -0.1323)




def scenario_d():
""" Tests the speed of math.modf """
for _ in LOOPS:
f, i = math.modf(X)  # (-0.13230000000000075, -100.0)




def scenario_e():
""" Tests the speed of math.modf when the integer part should be integer """
for _ in LOOPS:
f, i = math.modf(X)  # (-0.13230000000000075, -100.0)
i = int(i)  # -100




if __name__ == '__main__':
cProfile.run('scenario_a()')
cProfile.run('scenario_b()')
cProfile.run('scenario_c()')
cProfile.run('scenario_d()')
cProfile.run('scenario_e()')

结果:

         4 function calls in 1.357 seconds


Ordered by: standard name


ncalls  tottime  percall  cumtime  percall filename:lineno(function)
1    0.000    0.000    1.357    1.357 <string>:1(<module>)
1    1.357    1.357    1.357    1.357 test.py:11(scenario_a)
1    0.000    0.000    1.357    1.357 {built-in method builtins.exec}
1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}




4 function calls in 1.858 seconds


Ordered by: standard name


ncalls  tottime  percall  cumtime  percall filename:lineno(function)
1    0.000    0.000    1.858    1.858 <string>:1(<module>)
1    1.858    1.858    1.858    1.858 test.py:18(scenario_b)
1    0.000    0.000    1.858    1.858 {built-in method builtins.exec}
1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}




5000004 function calls in 2.744 seconds


Ordered by: standard name


ncalls  tottime  percall  cumtime  percall filename:lineno(function)
1    0.000    0.000    2.744    2.744 <string>:1(<module>)
1    1.245    1.245    2.744    2.744 test.py:26(scenario_c)
5000000    1.499    0.000    1.499    0.000 test.py:29(modf)
1    0.000    0.000    2.744    2.744 {built-in method builtins.exec}
1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}




5000004 function calls in 1.904 seconds


Ordered by: standard name


ncalls  tottime  percall  cumtime  percall filename:lineno(function)
1    0.000    0.000    1.904    1.904 <string>:1(<module>)
1    1.073    1.073    1.904    1.904 test.py:37(scenario_d)
1    0.000    0.000    1.904    1.904 {built-in method builtins.exec}
5000000    0.831    0.000    0.831    0.000 {built-in method math.modf}
1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}




5000004 function calls in 2.547 seconds


Ordered by: standard name


ncalls  tottime  percall  cumtime  percall filename:lineno(function)
1    0.000    0.000    2.547    2.547 <string>:1(<module>)
1    1.696    1.696    2.547    2.547 test.py:43(scenario_e)
1    0.000    0.000    2.547    2.547 {built-in method builtins.exec}
5000000    0.851    0.000    0.851    0.000 {built-in method math.modf}
1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}

使用 C/C + + 扩展

我尝试用 C/C + + 支持来编译这两个语句,结果甚至更好。使用 Python 扩展模块,可以获得比 math.modf更快、更精确的方法。

返回文章页面

def modf(number):
cdef float num = <float> number
cdef int i = <int> num
return i, (num*1e17 - i*1e17) / 1e17

参见 Cython 基础

test.py:

#!/usr/bin/env python
import math
import cProfile
import math2


""" Get the performance of both statements and math.modf """


X = -100.1323  # The number to be divided into integers and fractions
LOOPS = range(5 * 10 ** 6)  # Number of loops




def scenario_a():
""" Tests the speed of the statements in a function using C/C++ support """
for _ in LOOPS:
i, f = math2.modf(X)  # (-100, -0.1323)




def scenario_b():
""" Tests the speed of math.modf """
for _ in LOOPS:
f, i = math.modf(X)  # (-0.13230000000000075, -100.0)




if __name__ == '__main__':
cProfile.run('scenario_a()')
cProfile.run('scenario_b()')

结果:

         5000004 function calls in 1.629 seconds


Ordered by: standard name


ncalls  tottime  percall  cumtime  percall filename:lineno(function)
1    0.000    0.000    1.629    1.629 <string>:1(<module>)
1    1.100    1.100    1.629    1.629 test.py:10(scenario_a)
1    0.000    0.000    1.629    1.629 {built-in method builtins.exec}
5000000    0.529    0.000    0.529    0.000 {math2.modf}
1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}




5000004 function calls in 1.802 seconds


Ordered by: standard name


ncalls  tottime  percall  cumtime  percall filename:lineno(function)
1    0.000    0.000    1.802    1.802 <string>:1(<module>)
1    1.010    1.010    1.802    1.802 test.py:16(scenario_b)
1    0.000    0.000    1.802    1.802 {built-in method builtins.exec}
5000000    0.791    0.000    0.791    0.000 {built-in method math.modf}
1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}

注意

使用模可以使语句运算速度更快,但是不能使用模将负数分成整数和小数部分。

i, f = int(x), x*1e17%1e17/1e17 # Divide a number (x) into integer and fraction

例如,值 100.1323的正值或负值将被分为:
100.1323-> (1000.1323)
-100.1323-> (-1000.8677) < ——错

This will accomplish the task without the issue of dropping leading zeroes (like holydrinker's answer):

密码

def extract_int_decimal():
'''get the integer and decimal parts of a given number
by converting it to string and using split method
'''
num = 1234.5678
split_num = str(num).split('.')
int_part = int(split_num[0])
decimal_part = int(split_num[1]) * 10 ** -len(split_num[1])
print("integer part:",int_part)
print("decimal part:",decimal_part)


extract_int_decimal()

结果

integer part: 1234
decimal part: 0.5678000000000001