截断在Python中的花车

我想从浮点数中删除数字,在点后面有一个固定的数字位数,如:

1.923328437452 -> 1.923 

我需要输出为另一个函数的string,而不是打印。

另外我想忽略丢失的数字,而不是围绕它们。

首先,对于那些只需要一些复制粘贴代码的函数:

 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+中是有效的。 对于较老的版本,不可能得到相同的“智能舍入”效果(至less不是没有很多复杂的代码),但在截断之前舍入到小数点后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]]) 

说明

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

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

或者decimal模块

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

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

 0011111111010011001100110011001100110011001100110011001100110011 

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

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

 0.2999999999999999888977697537484345957637... 

所以一个天真的实现会拿出0.2即使这可能不是你想要的。 有关浮点表示法错误的更多信息, 请参阅Python教程 。

使用非常接近整数的浮点值非常罕见,但有意不等于该整数。 所以在截断的时候,select所有可能对应于内存值的“最好的”十进制表示是很有意义的。 Python 2.7及更高版本(但不包括3.0)包含一个复杂的algorithm来做到这一点 ,我们可以通过默认的string格式化操作访问。

 '{}'.format(f) 

唯一需要注意的是,这个行为就像g格式规范,如果数字足够大或者足够小的话,它就会使用指数符号( 1.23e+4 )。 所以该方法必须抓住这个案例,并以不同的方式处理。 有一些情况下,使用f格式规范,而不是导致一个问题,如试图截断3e-10到28位数的精度(它产生0.0000000002999999999999999980 ),我还不知道如何最好地处理这些。

如果你实际上正在使用非常接近整数的float s,但是有意不等于它们(比如0.29999999999999998或99.959999999999994),这会产生一些误报,也就是说它会舍入你不想舍入的数字。 在这种情况下,解决scheme是指定一个固定的精度。

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

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

在Python的早期版本(最高2.6或3.0)中,浮点数格式更为粗糙,并会定期生成

 >>> 1.1 1.1000000000000001 

如果这是你的情况,如果你想使用“好”的十进制表示截断,所有你可以做的(据我所知)是挑选一些数字,小于float表示的全部精度,截断它之前的那个数字。 典型的select是12,

 '%.12f' % f 

但你可以调整这个来适应你使用的数字。


1呃…我撒谎了。 从技术上讲,您可以指示Python重新parsing自己的源代码,并提取与传递给截断函数的第一个参数相对应的部分。 如果这个参数是一个浮点数字,你可以把它从小数点后面的一定数量的地方剪下来并返回。 然而,如果参数是一个variables,这个策略是行不通的,这使得它变得毫无用处。 以下仅供娱乐价值使用:

 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 

推广这个来处理你传递一个variables的情况看起来像是一个失败的原因,因为你必须通过程序的执行向后追溯,直到find给variables赋值的浮点数字。 如果有的话。 大多数variables将从用户input或mathexpression式初始化,在这种情况下,二进制表示就是全部。

 round(1.923328437452, 3) 

请参阅标准types的Python文档 。 您需要向下滚动一下以获得轮function。 本质上第二个数字表示将其舍入到多less个小数位数。

round的结果是一个浮动,所以注意:

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

使用格式化的string时你会更好:

 >>> "%.3f" % 1.923328437452 '1.923' >>> "%.3f" % 1.23456 '1.235' 
 n = 1.923328437452 str(n)[:4] 
 def trunc(num, digits): sp = str(num).split('.') return '.'.join([sp[0], sp[:digits]]) 

这应该工作。 它应该给你你正在寻找的截断。

这个问题的答案很多都是完全错误的。 他们要么收集花车(而不是截断)或不适用于所有情况。

当我search“Python truncate float”时,这是Google的最好结果,这个概念非常简单,值得更好的解答。 我同意Hatchkins使用decimal模块是pythonic这样做的方式,所以我给这里一个函数,我认为这个问题的答案是正确的,而且在所有情况下按预期工作。

作为一个侧面提示,小数值通常不能用二进制浮点variables来完全表示(参见这里的讨论),这就是为什么我的函数返回一个string。

 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() 

真正的pythonic这样做的方式是

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

在我的Python 2.7提示符下:

>>> int(1.923328437452 * 1000)/1000.0 1.923

简单的python脚本 –

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

我做了这样的事情:

 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(f, n): return math.floor(f * 10 ** n) / 10 ** n 

testing:

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

只是想提一下旧的“让()与()()”的诀窍

 round(f) = floor(f+0.5) 

可以从round()转过来做floor()

 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)) 

INT(16.5); 这将给出一个整数值16,即trunc,将不能够指定小数,但猜你可以做到这一点

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

使用numpy.round

 import numpy as np precision = 3 floats = [1.123123123, 2.321321321321] new_float = np.round(floats, precision) 
 def trunc(f,n): return ('%.16f' % f)[:(n-16)] 

一个简单而通用的函数:

 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 

这是一个简单的方法:

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

对于num = 1.923328437452,则输出1.923

>>> floor((1.23658945)* 10 ** 4)/ 10 ** 4
1.2365

划分并乘以10 **所需数字的数量

我也是一个python新手,在这里使用了一些零碎的东西,我提供了我的两分钱

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

str(int(time.time()))将时间纪元作为整数,并将其转换为string并join… str(datetime.now()。微秒)[:3]仅返回微秒,转换string和截断到前3个字符

 # value value to be truncated # n number of values after decimal value = 0.999782 n = 3 float(int(value*1en))*1e-n 

如果你的意思是打印时,那么下面的内容应该是正常的

 print '%.3f' % number