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]])
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]])
使用非常接近一个整数但故意不等于该整数的浮点值是非常罕见的。因此,在截断时,从所有可能与内存中的值相对应的十进制表示中选择“最好的”十进制表示可能是有意义的。Python2.7及更高版本(但不是3.0)包含一个sophisticated algorithm to do just that,我们可以通过默认的字符串格式化操作访问它。
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
round
的结果是一个浮点数,因此请注意(示例来自Python 2.6):当使用格式化字符串时,您会更好:
见Python's documentation on the standard types。你需要向下滚动一点才能进入圆形函数。实际上,第二个数字表示四舍五入到多少个小数位。
首先,函数,对于那些只想复制和粘贴代码的人:
这在Python2.7和3.1+中有效。对于较旧的版本,不可能获得相同的“智能舍入”效果(至少,不是没有很多复杂的代码),但是在截断之前舍入到12个小数位将在大部分时间内起作用:
解释
底层方法的核心是以全精度将值转换为字符串,然后切掉超出所需字符数的所有内容。后面的步骤很简单;可以通过字符串操作来完成
或者
decimal
模块第一步,转换成一个字符串,是相当困难的,因为有一些浮点文字对(即你在源代码中写的东西),它们都产生相同的二进制表示,但应该以不同的方式截断。例如,考虑0.3和0.299999999999998。如果您在Python程序中编写
0.3
,编译器会使用IEEE浮点格式将其编码为位序列(假设是64位浮点)这是最接近0.3的值,可以精确地表示为IEEE浮点。但是,如果您在Python程序中编写
0.29999999999999998
,编译器会将其转换为与完全相同的值。在一种情况下,您的意思是它被截断为0.3
,而在另一种情况下,您的意思是它被截断为0.2
,但是Python只能给出一个答案。这是Python的一个基本限制,或者说是任何没有延迟计算的编程语言。截断函数只能访问存储在计算机内存中的二进制值,而不能访问您在源代码中实际键入的字符串。1如果再次使用IEEE 64位浮点格式将位序列解码为十进制数,则
所以一个天真的实现会产生
0.2
,尽管这可能不是你想要的。有关浮点表示错误的详细信息,see the Python tutorial。使用非常接近一个整数但故意不等于该整数的浮点值是非常罕见的。因此,在截断时,从所有可能与内存中的值相对应的十进制表示中选择“最好的”十进制表示可能是有意义的。Python2.7及更高版本(但不是3.0)包含一个sophisticated algorithm to do just that,我们可以通过默认的字符串格式化操作访问它。
唯一需要注意的是,这就像一个
g
格式规范,即如果数字足够大或足够小,它使用指数符号(1.23e+4
)。所以这个方法必须抓住这个问题,并以不同的方式处理它。有一些情况下,使用f
格式规范反而会导致问题,例如试图将3e-10
截断为28位精度(它产生0.0000000002999999999999999980
),我还不确定如何最好地处理这些问题。如果实际上使用非常接近整数但故意不等于整数的
float
s(例如0.29999999999999998或99.95999999999994),这将产生一些误报,即它将舍入您不希望舍入的数字。在这种情况下,解决方案是指定固定精度。这里使用的精度位数并不重要,它只需要足够大,以确保在字符串转换中执行的任何舍入不会将值“提升”为其漂亮的十进制表示。我认为
sys.float_info.dig + n + 2
在所有情况下都是足够的,但如果不是这样的话2
可能就必须增加,这样做并不有害。在早期版本的Python(高达2.6或3.0)中,浮点数格式要粗糙得多,通常会产生如下内容
如果这是你的情况,如果你想使用“nice”十进制对于截断的表示,您所能做的(据我所知)就是选择一些位数,小于可以用
float
表示的全精度,并在截断之前将该位数舍入到该位数。一个典型的选择是12但是你可以调整这个以适应你正在使用的数字。
1好吧。。。我撒谎了。技术上,您可以指示Python重新解析它自己的源代码,并提取与传递给truncation函数的第一个参数对应的部分。如果该参数是浮点文字,则只需将其截断小数点后的某个位置,然后返回。但是,如果参数是变量,则此策略不起作用,这使得它相当无用。以下仅为娱乐价值:
将其泛化以处理传入变量的情况似乎是一个失败的原因,因为在找到赋予变量值的浮点文字之前,必须向后跟踪程序的执行过程。如果真的有。大多数变量将从用户输入或数学表达式初始化,在这种情况下,二进制表示就是全部。
相关问题 更多 >
编程相关推荐