【问题标题】:Print floating point numbers with variable precision以可变精度打印浮点数
【发布时间】:2021-07-25 16:29:34
【问题描述】:

我想打印包含可变精度的浮点数。

我有多个数字,例如:

0.634564644534135499
0.0005462007746487777
0.028820785252590582
0.0018751147995774936
0.0075146048125540816
0.00046670455

我想使用 print 获得与输出相同的数字。我知道可以使用print("{:.19f}".format(0.0005462007746487777)) 固定小数位数,但我不想固定小数位数。因为不同的数字会有不同的小数位

代码

#!/usr/bin/env python3
 
number_1=0.634564644534135499
number_2=0.0005462007746487777
number_3=0.028820785252590582
number_4=0.0018751147995774936
number_5=0.0075146048125540816
number_6=0.00046670455

print("Number 1: ",number_1)
print("Number 2: ",number_2)
print("Number 3: ",number_3)
print("Number 4: ",number_4)
print("Number 5: ",number_5)
print("Number 6: ",number_6)

实际输出:

Number 1:  0.6345646445341355
Number 2:  0.0005462007746487777
Number 3:  0.028820785252590582
Number 4:  0.0018751147995774936
Number 5:  0.0075146048125540816
Number 6:  0.00046670455

所需输出:

Number 1: 0.634564644534135499
Number 2: 0.0005462007746487777
Number 3: 0.028820785252590582
Number 4: 0.0018751147995774936
Number 5: 0.0075146048125540816
Number 6: 0.00046670455

我不明白为什么 Number 2(精度更高)打印正确,但 Number 1 失去精度?

【问题讨论】:

  • 这归结为python用于浮点十进制字符串表示的算法。它使用 David Gay 的算法来寻找不会改变其值的最短浮点表示。无法取回“您输入的数字”,因为许多可能的十进制文字将映射到实际的浮点表示。
  • 您是否理解,从根本上说,您输入为浮点文字的许多十进制数根本无法表示?这归结为机器表示是二进制的,以及浮动对象的固定大小性质,这些对象基本上是 C double 的包装器。最重要的是float 对象是如何打印的问题。
  • 为什么需要完全相同的数字?
  • @Daniel 我需要它们来进行测试。我有 2 个应用程序。一个 C++ 和一个 python(这是一个用于测试 C++ 不同功能的参考应用程序)。两个应用程序都在 json 消息中接收这些数字,并且都必须打印 json 消息的每个字段。 C++ 以正确的方式打印数字,而 python 有这个限制
  • @Saad_Khan:没有一种正确的方法。从某种意义上说,Python 更正确,因为它保证它使用最少的小数位。不同的 C++ 实现可以使用不同的方法并导致不同的结果。在比较结果时,您必须处理同一数字的不同表示。

标签: python python-3.x floating-point precision


【解决方案1】:

所以这里有一个一般数字的参考图片(从这个链接https://www.log2base2.com/number-system/float-to-binary-conversion.html 摘自网络):

Python 使用每个位来存储整数部分和小数部分,并使用 64 位来存储浮点值。因此,如果整数部分(绝对值)很高,那么它自动意味着小数部分将具有较少的存储位。

在您提供的示例中,由于每个值的整数部分都是0,因此可以完全在小数部分进行比较。

因此,对于这种情况,我们将只查看小数部分中的值。

在第一个示例中(0.634564644534135499),小数点后的值的数量虽然少于第二种情况(0.0005462007746487777),但第一种情况所需的位数比第二种情况更多。所以存储的值会被四舍五入,以确保位数符合python中的要求。

所以要改变这一点,我能想到的一种方法是以字符串的形式将值存储在引号内。它会起作用,但假设您将从其他地方获取数据,不确定这是否非常好:

number_1="0.634564644534135499"
# number_3=0.028820785252590582
# number_4=0.0018751147995774936
# number_5=0.0075146048125540816
# number_6=0.00046670455

print("Number 1: ",number_1)
# print("Number 2: ",number_2)

另一种方法是使用 decimal.Decimal('0.634564644534135499')。如果我还没有回答您的疑问,至于为什么会这样,请告诉我,我会尽力解释得更好。

【讨论】:

  • 感谢您的解释。我从其他地方以浮点格式获取数据,因此我无法将其存储在引号中,并且转换为字符串也会导致它丢失实际数字。有没有其他你熟悉的方法可以用来打印实际数字
  • 您好真的很抱歉回复晚了。但这并不是那么简单。您可能必须在每个地方进行更改。由于假设您的数据经过多个站点,即使有一个地方是浮动的,它也会将该值四舍五入为 64 位。您是否可以控制数据源?
【解决方案2】:

从根本上说,float 对象无法满足您的要求,这些对象基本上是 C-double 的包装器,因此是 64 位浮点数。

这里发生了几件事。在源代码中映射十进制文字输入存在根本问题:

0.634564644534135499

转为实际的机器表示,不是十进制,而是二进制。

由于 64 位浮点格式的固有限制,多个十进制输入将映射到相同的底层表示:

>>> 0.634564644534135499 == 0.6345646445341355
True

实际上两者都不是,确切的小数实际上是:

>>> import decimal
>>> decimal.Decimal(0.6345646445341355)
Decimal('0.6345646445341355246227976749651134014129638671875')
>>> decimal.Decimal(0.634564644534135499)
Decimal('0.6345646445341355246227976749651134014129638671875')

当您天真地print(some_float) 时,实际打印的内容将是一种表示,如果您将其输入为浮点十进制文字,则保证映射回产生它的相同浮点数。事实上,since Python 3.1 CPython 使用 David Gray 的算法来寻找 最短 保留值的此类表示。

因此,由于float 的固有限制而遇到麻烦时,通常会尝试使用decimal.Decimal。请记住,输入应该是一个字符串。以下脚本:

import decimal
number_1 = decimal.Decimal('0.634564644534135499')
number_2 = decimal.Decimal('0.0005462007746487777')
number_3 = decimal.Decimal('0.028820785252590582')
number_4 = decimal.Decimal('0.0018751147995774936')
number_5 = decimal.Decimal('0.0075146048125540816')
number_6 = decimal.Decimal('0.00046670455')

print("Number 1: ",number_1)
print("Number 2: ",number_2)
print("Number 3: ",number_3)
print("Number 4: ",number_4)
print("Number 5: ",number_5)
print("Number 6: ",number_6)

打印:

Number 1:  0.634564644534135499
Number 2:  0.0005462007746487777
Number 3:  0.028820785252590582
Number 4:  0.0018751147995774936
Number 5:  0.0075146048125540816
Number 6:  0.00046670455

【讨论】:

  • 感谢您的详细解释。我不能完全控制输入。我从其他地方以浮点格式获取数据,我需要按原样打印它。如果还有其他您可能熟悉的方法可以用来打印实际值,请告诉我
  • @Saad_Khan 你说的实际值是什么意思?
  • @Saad_Khan 我强烈建议您阅读以下内容:docs.python.org/3/tutorial/floatingpoint.html 以及这里已经很好的答案。你所看到的你的机器可以做的最好的
  • @juanpa.arrivillaga 按实际值我的意思是我想打印0.634564644534135499 而不是0.6345646445341355
  • @Saad_Khan 基于什么?你需要仔细阅读我写的内容。
猜你喜欢
  • 1970-01-01
  • 2020-08-04
  • 2012-06-09
  • 1970-01-01
  • 2019-11-07
  • 2019-09-15
  • 2015-02-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多