python语言中浮点数_在Python中截断浮点数

首先,该功能适用于那些只想要一些复制粘贴代码的人: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。如果您0.3在Python程序中编写,则编译器使用IEEE浮点格式将其编码为位序列(假设为64位浮点数)0011111111010011001100110011001100110011001100110011001100110011

这是0.3的最接近的值,可以准确地表示为IEEE浮点数。但是如果你0.29999999999999998在Python程序中编写,编译器会将其转换为完全相同的值。在一种情况下,你的意思是它被截断(到一位数)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),而我还不确定如何最好地处理这些问题。

如果你确实正在与工作floats表示非常接近圆形数字,但故意不等于他们(像0.29999999999999998或99.959999999999994),这会产生一些假阳性,即它会圆你不想四舍五入数值。在这种情况下,解决方案是指定固定的精度。'{0:.{1}f}'.format(f, sys.float_info.dig + n + 2)

这里使用的精度位数并不重要,它只需要足够大,以确保在字符串转换中执行的任何舍入不会将值“提升”到其漂亮的十进制表示。我认为sys.float_info.dig + n + 2在所有情况下都可能足够,但如果不是这样2可能必须增加,并且这样做也没有坏处。

在Python的早期版本(高达2.6或3.0)中,浮点数格式化更粗糙,并且会定期生成像>>> 1.11.1000000000000001

如果这是你的情况,如果你不希望使用“好”十进制表示为截断,所有你能做的(据我所知)是挑选一些号码的数字,小于由全精度表示的float,与轮在截断之前编号为那么多位数。典型的选择是12,'%.12f' % f

但你可以调整它以适应你正在使用的数字。

1嗯......我撒了谎。从技术上讲,您可以指示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

推广这个以处理传入变量的情况似乎是一个失败的原因,因为你必须追溯程序的执行,直到找到给变量赋值的浮点文字。如果有的话。大多数变量将从用户输入或数学表达式初始化,在这种情况下,二进制表示就是全部。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值