截断f-string中的浮点数而不四舍五入

7
我想打印非常接近1的浮点数,并截断为两位小数,而不进行四舍五入,最好用尽量少的代码实现。
a = 0.99999999999
print(f'{a:0.2f}')

Expected: 0.99
Actual: 1.00

3
乘以100,使用math.floor()向下取整,然后除以100。 - Barmar
@Barmar 或许可以使用 trunc 而不是 floor,这样也可以处理负数了?(如果输入已知接近于1且为正数,则无关紧要) - chtz
2
当数字“非常接近于1”时,乘以100会导致四舍五入,可能形成一个等于100.0的乘积。相反,将舍入模式设置为向0舍入,然后打印到17个或更多小数位,并在文本上截断小数点后除了2位的所有内容。 - chux - Reinstate Monica
只是随便想想...如果 f'...:.2f' 应该格式化文本输出,那么它不应该四舍五入任何东西,或者至少提供这个选项。 - 101is5
3个回答

1

您也可以尝试这种方法,只需将数字转换为整数再转回浮点数即可截断小数部分:

num_decimals = 2
a = 0.99999999999
a = int(a * 10 ** num_decimals)/10 ** num_decimals

print(a) # => prints 0.99

1

如果我理解正确,我认为你不需要使用f-strings或数学函数。普通的字符串操作应该可以实现你的目标:

a = 0.987654321
print(str(a)[:4])

输出:

0.98

2
这对于0.99999999999999988897769753748434595763683319091796875失败了。然后str(a)会产生“1.0”。 - Eric Postpischil
3
0.9899999999999999911182158029987476766109466552734375是一个例子,它在目前常见的Python实现中会出现错误。这个值可以用IEEE-754二进制64位表示,这是一种常用的表示方法,但Python的str可能会将其显示为“0.99”,然后问题中的代码就会错误地显示“0.99”而不是“0.98”。(需要注意的是,与您声称的无舍入相反,实际上确实进行了四舍五入,因为传递给它的实际浮点值不是0.987654321,而是0.98765432099999994619565768516622483730316162109375。) - Eric Postpischil
我认为我有一个无法通过这种方法的案例:a = 0.87。由于它不能被精确地保存为float,因此使用最接近的值:0.8699999999999999955591...。我怀疑你的str(a)将其打印为四舍五入的17(或更少)个有效位数或0.87000000000000000。用[:4]截取会得到"0.87",而根据OP的“截断到2位小数而不进行四舍五入”,应该是"0.86" - chux - Reinstate Monica
在我的情况下,我得到的是0.9999999999999999; 所以尾数被四舍五入了,但是前面一个小数点没有。 - Jack Fleeting
@chux-ReinstateMonica 的确;a = 0.8699999999999999 是最长的浮点数,仍然截断为 0.86;再加一个 9(或任何大于 4 的数字),它就会截断为 0.87 - Jack Fleeting
显示剩余6条评论

0

我在Python方面的技能还不够熟练,但浮点数运算与我熟悉的C语言非常接近。

Python的float和C的double一样是基于2进制的,所以像0.xx这样的值,除了0.00, 0.25, 0.50, 0.75之外,无法被准确编码。

让我们尝试a=0.87。最接近可表示的Python float值为:

 # 1234567890123456789012  
 0.8699999999999999955591...

因此,真实值小于0.87。

使用少于18位数字打印(似乎是str()的结果)会导致四舍五入的输出。

 # 12345678901234567
 0.87000000000000000

对文本进行截断会导致"0.87",并不符合“将其截断为2位小数而不四舍五入”的要求,因为原始值是0.8699999999999999955591...

要解决这个问题,代码需要在调用str()之前更改float数学的舍入模式以朝向0舍入而不是四舍五入或者使用额外精度转换为字符串。我怀疑Python可以做到这一点-我不确定。

即使使用额外精度(仍然使用四舍五入),在一般情况下,仍可能存在真实值为xxx.xx(很多9)...... 这会进行四舍五入并阻止我们的文本截断。


关于“我怀疑Python可以做到这一点-我不确定。”:Python允许使用大量精度请求格式(如%.99g%x),某些实现可能会正确执行此操作。但是,Python对浮点数很宽松;文档没有包含有关其特性或行为的强烈声明。是否正确实现了将多个数字转换为字符串未指定,因此不能依赖此功能。而且我认为Python没有控制舍入模式的规定。 - Eric Postpischil
基本上,如果你想要可靠的浮点数算术运算,Python 不是一个适合使用的语言。 - Eric Postpischil
1
@EricPostpischil 感谢您提供的 Python 输入。如果没有控制舍入模式的规定,这项任务很难处理所有情况。关于“是否正确实现了将许多数字转换为字符串”,这也是一个编码漏洞。我希望 Python 能够朝着正确的 IEEE 754 规范至少 20 位数字的 float 转换为字符串方向发展。 - chux - Reinstate Monica

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接