如何截断浮点值?

我想从浮点数中移除数字,以便在点后面有固定数目的数字,比如:

1.923328437452 → 1.923

我需要以字符串的形式输出到另一个函数,而不是打印。

另外,我想忽略丢失的数字,而不是四舍五入。

307703 次浏览
round(1.923328437452, 3)

参见 Python 关于标准类型的文档。您需要向下滚动一点才能看到 round 函数。本质上,第二个数字表示四舍五入到多少个小数位。

如果你的意思是在打印时,那么以下应该工作:

print '%.3f' % number

round的结果是一个 float,所以要小心(例如来自 Python 2.6) :

>>> round(1.923328437452, 3)
1.923
>>> round(1.23456, 3)
1.2350000000000001

使用格式化字符串会更好:

>>> "%.3f" % 1.923328437452
'1.923'
>>> "%.3f" % 1.23456
'1.235'

首先,对于那些只想复制粘贴代码的人来说,这个函数是:

def truncate(f, n):
'''Truncates/pads a float f to n decimal places without rounding'''
s = '{}'.format(f)
if 'e' in s or 'E' in s:
return '{0:.{1}f}'.format(f, n)
i, p, d = s.partition('.')
return '.'.join([i, (d+'0'*n)[:n]])

这在 Python 2.7和3.1 + 中是有效的。对于旧版本,不可能获得同样的“智能舍入”效果(至少,如果没有大量复杂的代码就不可能) ,但是在截断之前舍入到小数点后12位将在大多数情况下有效:

def truncate(f, n):
'''Truncates/pads a float f to n decimal places without rounding'''
s = '%.12f' % f
i, p, d = s.partition('.')
return '.'.join([i, (d+'0'*n)[:n]])

解释

底层方法的核心是以完全精确的方式将值转换为字符串,然后删除所有超出所需字符数的内容。后一步很简单; 可以通过字符串操作来完成

i, p, d = s.partition('.')
'.'.join([i, (d+'0'*n)[:n]])

或者 decimal模块

str(Decimal(s).quantize(Decimal((0, (1,), -n)), rounding=ROUND_DOWN))

第一步,转换为字符串,是相当困难的,因为有一些浮点文字对(即您在源代码中编写的内容) ,它们都产生相同的二进制表示,但是应该以不同的方式截断。例如,考虑0.3和0.29999999999999998。如果在 Python 程序中编写 0.3,编译器将使用 IEEE 浮点格式将其编码为位序列(假设浮点数为64位)

0011111111010011001100110011001100110011001100110011001100110011

这是最接近0.3的值,可以准确地表示为 IEEE 浮点数。但是如果在 Python 程序中编写 0.29999999999999998,编译器会将其转换为 完全相同的价值。在一种情况下,您希望将其截断为 0.3(一个数字) ,而在另一种情况下,您希望将其截断为 0.2,但 Python 只能给出一个答案。这是 Python 的一个根本限制,或者说是任何没有惰性计算的编程语言的一个根本限制。截断函数只能访问存储在计算机内存中的二进制值,而不能访问您实际键入到源代码.1中的字符串

如果再次使用 IEEE 64位浮点格式将位序列解码为十进制数,则会得到

0.2999999999999999888977697537484345957637...

因此,即使 0.2可能不是您想要的,也会出现一个幼稚的实现。有关浮点表示错误的详细信息,请参阅 请参阅 Python 教程

使用一个如此接近整数的浮点值,而且 故意的不等于该整数,这种情况非常罕见。因此,在截断时,从所有可能对应于内存值的小数中选择“最好的”可能是有意义的。Python 2.7和更高版本(但不是3.0)包含一个 复杂的算法来做到这一点,我们可以通过默认的字符串格式化操作访问它。

'{}'.format(f)

唯一需要注意的是,这类似于 g格式规范,如果数字足够大或足够小,则使用指数表示法(1.23e+4)。所以方法必须捕获这种情况,并以不同的方式处理它。在一些情况下,使用 f格式规范会导致问题,比如试图将 3e-10截断为28位精度(它产生 0.0000000002999999999999999980) ,我还不确定如何最好地处理这些问题。

如果你实际使用的 float非常接近整数,但故意不等于它们(比如0.299999999999999998或99.959999999999999999999999) ,这将产生一些假阳性,也就是说,它将整数,你不想四舍五入。在这种情况下,解决方案是指定一个固定的精度。

'{0:.{1}f}'.format(f, sys.float_info.dig + n + 2)

这里使用的精度数字并不重要,它只需要足够大,以确保在字符串转换中执行的任何舍入操作都不会“增加”该值到其良好的小数。我认为 sys.float_info.dig + n + 2在任何情况下都足够了,但是如果不够的话,2可能需要增加,这样做也没有坏处。

在 Python 的早期版本(最多2.6或3.0)中,浮点数格式化要粗糙得多,并且会定期生成诸如

>>> 1.1
1.1000000000000001

如果这是你的情况,如果你的 想使用“好”的十进制表示截断,你所能做的(据我所知)是选择一些数字,小于完整的精度表示由 float,并四舍五入到那么多位数之前,截断它。典型的选择是12,

'%.12f' % f

但是你可以根据你使用的数字来调整它。


好吧... 我撒谎了。从技术上讲,可以指示 Python 重新解析自己的源代码,并提取与传递给截断函数的第一个参数相对应的部分。如果这个参数是一个浮点文字,那么可以在小数点后面截去一定数量的位置,然后返回这个值。然而,如果参数是一个变量,这种策略就不起作用,这使得它相当无用。下列内容仅供娱乐之用:

def trunc_introspect(f, n):
'''Truncates/pads the float f to n decimal places by looking at the caller's source code'''
current_frame = None
caller_frame = None
s = inspect.stack()
try:
current_frame = s[0]
caller_frame = s[1]
gen = tokenize.tokenize(io.BytesIO(caller_frame[4][caller_frame[5]].encode('utf-8')).readline)
for token_type, token_string, _, _, _ in gen:
if token_type == tokenize.NAME and token_string == current_frame[3]:
next(gen) # left parenthesis
token_type, token_string, _, _, _ = next(gen) # float literal
if token_type == tokenize.NUMBER:
try:
cut_point = token_string.index('.') + n + 1
except ValueError: # no decimal in string
return token_string + '.' + '0' * n
else:
if len(token_string) < cut_point:
token_string += '0' * (cut_point - len(token_string))
return token_string[:cut_point]
else:
raise ValueError('Unable to find floating-point literal (this probably means you called {} with a variable)'.format(current_frame[3]))
break
finally:
del s, current_frame, caller_frame

通过泛化这种方法来处理传入变量的情况似乎是一种失败的做法,因为您必须向后追踪程序的执行过程,直到找到赋予变量值的浮点文字。如果真有的话。大多数变量将从用户输入或数学表达式初始化,在这种情况下,只有二进制表示。

def trunc(num, digits):
sp = str(num).split('.')
return '.'.join([sp[0], sp[1][:digits]])

这个应该可以,它应该会给你你要找的截断。

def trunc(f,n):
return ('%.16f' % f)[:(n-16)]

只是想提一下“用地板()转()”的老把戏

round(f) = floor(f+0.5)

可以转过来使地板()从圆()

floor(f) = round(f-0.5)

尽管这两条规则都打破了负数,因此使用它并不理想:

def trunc(f, n):
if f > 0:
return "%.*f" % (n, (f - 0.5*10**-n))
elif f == 0:
return "%.*f" % (n, f)
elif f < 0:
return "%.*f" % (n, (f + 0.5*10**-n))
n = 1.923328437452
str(n)[:4]

Int (16.5) ; 这将给出一个16的整数值,也就是 trunc,它不能指定小数,但是我猜你可以通过

import math;


def trunc(invalue, digits):
return int(invalue*math.pow(10,digits))/math.pow(10,digits);

我也是一个蟒蛇新手,在利用了这里的一些零碎信息后,我提供了我的两分钱

print str(int(time.time()))+str(datetime.now().microsecond)[:3]

Str (int (time.time ())将把时间纪元作为 int 并将其转换为 string 并与..。 Str (datetime.now () . microsecond)[ : 3]只返回微秒,转换为 string 并截断为前3个字符

真正的蟒蛇式方法是

from decimal import *


with localcontext() as ctx:
ctx.rounding = ROUND_DOWN
print Decimal('1.923328437452').quantize(Decimal('0.001'))

或更短:

from decimal import Decimal as D, ROUND_DOWN


D('1.923328437452').quantize(D('0.001'), rounding=ROUND_DOWN)

更新

通常问题不在于截断浮点数本身,而在于不恰当地使用浮点数 之前舍入。

例如: int(0.7*3*100)/100 == 2.09

如果你是 被迫的使用浮点数(比如说,你正在用 numba加速你的代码) ,最好使用分作为价格的“内部表示”: (70*3 == 210)和乘/除输入/输出。

我做了这样的事:

from math import trunc




def truncate(number, decimals=0):
if decimals < 0:
raise ValueError('truncate received an invalid value of decimals ({})'.format(decimals))
elif decimals == 0:
return trunc(number)
else:
factor = float(10**decimals)
return trunc(number*factor)/factor

这里有一个简单的方法:

def truncate(num, res=3):
return (floor(num*pow(10, res)+0.5))/pow(10, res)

对于 num = 1.923328437452,则输出为1.923

这个问题的许多答案都是完全错误的。它们要么四舍五入浮点数(而不是截断) ,要么不适用于所有情况。

这是我搜索“ Python truncate float”时的 Google 搜索结果,这个概念非常简单,值得给出更好的答案。我同意哈奇金斯的观点,使用 decimal模块是这样做的 Python 方法,所以我在这里给出了一个函数,我认为它正确地回答了问题,并且在所有情况下都能正常工作。

值得注意的是,小数值通常不能由二进制浮点变量精确表示(参见 给你对此的讨论) ,这就是为什么我的函数返回一个字符串。

from decimal import Decimal, localcontext, ROUND_DOWN


def truncate(number, places):
if not isinstance(places, int):
raise ValueError("Decimal places must be an integer.")
if places < 1:
raise ValueError("Decimal places must be at least 1.")
# If you want to truncate to 0 decimal places, just do int(number).


with localcontext() as context:
context.rounding = ROUND_DOWN
exponent = Decimal(str(10 ** - places))
return Decimal(str(number)).quantize(exponent).to_eng_string()

你可以这样做:

def truncate(f, n):
return math.floor(f * 10 ** n) / 10 ** n

测试:

>>> f=1.923328437452
>>> [truncate(f, n) for n in range(5)]
[1.0, 1.9, 1.92, 1.923, 1.9233]

用 numpy.round

import numpy as np
precision = 3
floats = [1.123123123, 2.321321321321]
new_float = np.round(floats, precision)
# value  value to be truncated
# n  number of values after decimal


value = 0.999782
n = 3
float(int(value*1en))*1e-n

简单的 Python 脚本-

n = 1.923328437452
n = float(int(n * 1000))
n /=1000

使用一个通用而简单的函数:

def truncate_float(number, length):
"""Truncate float numbers, up to the number specified
in length that must be an integer"""


number = number * pow(10, length)
number = int(number)
number = float(number)
number /= pow(10, length)
return number

在我的 Python 2.7提示中:

> > 税务局网页 > 税务局网页 > 税务局网页 > 税务局网页 > 税务局网页 > 税务局网页 > 税务局网页(1.92328437452 * 1000)/1000.0 1.923

>>> from math import floor
>>> floor((1.23658945) * 10**4) / 10**4
1.2365

# 除以10 * * 所需数字的数目

如果你喜欢一些数学,这对 + ve 数有效:

>>> v = 1.923328437452
>>> v - v % 1e-3
1.923

在 python3中有一个简单的解决方案。Where to cut 我用一个 help 变量 decPlace 定义,使其更容易适应。

f = 1.12345
decPlace= 4
f_cut = int(f * 10**decPlace) /10**decPlace

产出:

f = 1.1234

希望能有帮助。

def precision(value, precision):
"""
param: value: takes a float
param: precision: int, number of decimal places
returns a float
"""
x = 10.0**precision
num = int(value * x)/ x
return num
precision(1.923328437452, 3)

1.923

足够简单的东西,以适应列表理解,没有库或其他外部依赖。对于 Python > = 3.6,使用 f-string 编写非常简单。

这个想法是让字符串转换完成整数 去一个你不需要的地方,然后砍掉最后一个数字。

>>> nout = 3  # desired number of digits in output
>>> [f'{x:.{nout+1}f}'[:-1] for x in [2/3, 4/5, 8/9, 9/8, 5/4, 3/2]]
['0.666', '0.800', '0.888', '1.125', '1.250', '1.500']

当然,这里有 舍入(即对第四个数字) ,但是舍入 在某个时候是不可避免的。如果截断和舍入之间的转换是相关的,这里有一个稍微好一点的例子:

>>> nacc = 6  # desired accuracy (maximum 15!)
>>> nout = 3  # desired number of digits in output
>>> [f'{x:.{nacc}f}'[:-(nacc-nout)] for x in [2.9999, 2.99999, 2.999999, 2.9999999]]
>>> ['2.999', '2.999', '2.999', '3.000']

额外奖励: 去掉右边的零

>>> nout = 3  # desired number of digits in output
>>> [f'{x:.{nout+1}f}'[:-1].rstrip('0') for x in [2/3, 4/5, 8/9, 9/8, 5/4, 3/2]]
['0.666', '0.8', '0.888', '1.125', '1.25', '1.5']

在我看来,给出 给你的核心思想是解决这个问题的最佳方法。 不幸的是,它已经收到较少的投票,而 稍后回答有更多的投票是不完整的(正如在评论中所观察到的)。希望下面的实现为 截断 提供了一个简短的 < strong > 和 完整解决方案。

def trunc(num, digits):
l = str(float(num)).split('.')
digits = min(len(l[1]), digits)
return l[0] + '.' + l[1][:digits]

这应该照顾到所有角落病例发现 给你给你

简短易懂的变体

def truncate_float(value, digits_after_point=2):
pow_10 = 10 ** digits_after_point
return (float(int(value * pow_10))) / pow_10


>>> truncate_float(1.14333, 2)
>>> 1.14


>>> truncate_float(1.14777, 2)
>>> 1.14




>>> truncate_float(1.14777, 4)
>>> 1.1477

大多数答案在我看来都太复杂了,这个怎么样?

digits = 2  # Specify how many digits you want


fnum = '122.485221'
truncated_float = float(fnum[:fnum.find('.') + digits + 1])


>>> 122.48

只需扫描“ .”的索引并根据需要截断(不进行四舍五入)。 将字符串转换为 float 作为最后一步。

或者在您的情况下,如果您得到一个 float 作为输入,并希望一个字符串作为输出:

fnum = str(122.485221)  # convert float to string first
truncated_float = fnum[:fnum.find('.') + digits + 1]  # string output

当使用熊猫时,这对我很有用

import math
def truncate(number, digits) -> float:
stepper = 10.0 ** digits
return math.trunc(stepper * number) / stepper


df['trunc'] = df['float_val'].apply(lambda x: truncate(x,1))
df['trunc']=df['trunc'].map('{:.1f}'.format)

我认为更好的做法是找到小数点 .index,然后相应地取出字符串切片:

def truncate(number, n_digits:int=1)->float:
'''
:param number: real number ℝ
:param n_digits: Maximum number of digits after the decimal point after truncation
:return: truncated floating point number with at least one digit after decimal point
'''
decimalIndex = str(number).find('.')
if decimalIndex == -1:
return float(number)
else:
return float(str(number)[:decimalIndex+n_digits+1])
int(1.923328437452 * 1000) / 1000
>>> 1.923


int(1.9239 * 1000) / 1000
>>> 1.923

通过将数字乘以1000(3位数字为10 ^ 3) ,我们将小数点向右移动3位,得到 1923.3284374520001。当我们把它转换成一个整数时,小数部分 3284374520001将被丢弃。然后我们通过除以返回 1.923的1000来取消小数点的移位。