【问题标题】:printing double precision floating point numbers打印双精度浮点数
【发布时间】:2018-01-23 20:43:54
【问题描述】:

我试图在字符串中表示双精度,为此我使用了 grisu 的算法,你可以在这里查看:https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf

我已经完成了,显然正在工作,当我与 printf 输出进行高精度比较时,我的问题是:

double u = 1, t = 3;
double co = u/t;
printf("%.100f", co);

输出

0.3333333333333333148296162562473909929394721984863281250000000000000000000000000000000000000000000000

使用 grisu 的算法,我可以获得以下结果:

3333333333333333e-16

什么是逻辑,因为您可以使用 double 获得的最大精度为 21 位,您可以在 PDF 中的“5.3 C 实现”(第 6 页)中阅读。那么如果我的最大精度是这个,或者哪个算法正在使用 printf 来获得这个精度,我怎么能得到其余的呢?

【问题讨论】:

  • 您为什么认为 53 位二进制精度接近 21 位十进制精度?
  • 你的意思是用 53 位尾数我不能得到这个?
  • log10(pow(2, 53)) 不是21
  • @Ripi2 不,你确实不会得到垃圾。你得到 exact 值的 base-10 表示,就像它在 base-2 中一样。
  • @KeithThompson:C 2011 [N1570] 5.2.4.2.2 6:“浮点运算(+、-、*、/)和 是实现定义的,正如 。实现可能会声明准确性未知。”

标签: c algorithm floating-point double ieee-754


【解决方案1】:

浮点运算的基本原则,尤其是在 IEEE 754 标准中规定的那样,是浮点数据准确地表示一个特定的数字。

对浮点数执行运算时,精确的数学结果可能无法以浮点格式表示。在这种情况下,该操作根据某些规则返回最接近的可表示数字(通常舍入到最接近偶数位,但有时朝+无穷大舍入、朝-无穷大舍入、朝零舍入或舍入到奇数)。因此,浮点运算中的运算可能会返回近似结果,但数字是精确的。

在您的示例中,将 1 除以 3 得到的浮点数正好是 0.333333333333333314829616256247390992939472198486328125。这是因为数学结果正好是⅓,但是不能用二进制浮点数表示,而上面显示的数字是最接近的可以表示的值,所以是结果。

使用转换说明符%.100f 调用printf 请求将此二进制浮点值转换为十进制。这是一个数学运算,将二进制浮点值转换为十进制的数学结果将是“0.333333333333333314829616256247390992939472198486328125”。既然你告诉printf 使用 100 位数字,它就有足够的数字来产生准确的结果,所以它确实如此。

(这表明您正在使用高质量的printf 实现。某些实现无法正确执行此操作。)

您引用的 Florian Loitsch 的论文提供了将二进制浮点值转换为足够多的十进制数字的算法,以便将该值与相邻的浮点值区分开来。它通常不会生成足够的十进制数字来显示确切的值。例如,在两位数的 base-3 浮点中,我们可以表示数字 0、1/9、2/9、3/9、4/9 等。在这种情况下,如果值为 4/9 (.4444...),则打印“.4”就足以将值与 .3333... 和 .5555... 区分开来,但它不能完全代表该值。 Loitsch 的算法只产生足够的数字来区分值,通常不足以显示精确的数学值。

(Loitsch 的论文还讨论了算法产生区分值的最短结果的频率 - 刚好足够的数字来完成这项工作,仅此而已。)

关于基础之间转换的经典论文是 David M. Gay 的Correctly Rounded Binary-Decimal and Decimal-Binary Conversions

【讨论】:

  • @latiagertrutis:为什么你认为co 包含⅓?它包含将 1 除以 3 作为浮点运算的结果。由于二进制浮点不能表示⅓,因此结果不是⅓。相反,它是最接近的可表示值。
  • 但是我的变量 'co' 是一个包含值 1/3 的双精度数,他的尾数是:10101010101010101010101010101010101010101010101010101,那么 printf 如何获得这个近似值?
  • 为了避免错过理解,我知道有些数字不能用双精度的有限大小表示,我试图找到的是使用 printf 的方法。
  • @latiagertrutis:printf 打印的值不是近似值;它正是co 中的值。我不知道printf 使用的是哪种算法(我什至不知道您使用的是哪种printf——Apple 的?GCC 自带的?)。如果你想要一种算法来进行这种转换,请搜索 Gay 的论文。似乎有一个副本here
  • 您应该阅读 Loitsch 的文章,以及 Marc Andrysco、Ranjit Jhala、Sorin Lerner 的“打印浮点数:更快、始终正确的方法”。首先,这比猜测它们要好,其次,它们提供了确切的转换,无论好坏,这是某些编程语言转换为十进制功能的核心。 ideone.com/Mw09aj
【解决方案2】:

[that] printf 的输出是错误的精度,从某种意义上说,它告诉您二进制数在以十进制形式呈现时的精确值,就好像该二进制数最后有无限数量的 0 位一样,当我们知道这个数字最初可能以十进制开头时。

【讨论】:

  • (a) 精度不为假; IEEE 754 清楚地表明浮点数据恰好代表一个特定数字(除非它是 NaN),并且printf 已正确显示该数字。 (b) 关于 1 或 3 或 1/3 没有什么是特别小数的;这些在基数 4、7、16 或 233 中是相同的。并且 ⅓ 不能用十进制浮点表示,因此在某些方面假设值应该是十进制是不正确的。 IEEE 754 的原则很好:尽可能地保留值。这意味着在询问时显示所有数字,而不是任意截断小数。
猜你喜欢
  • 2012-06-09
  • 1970-01-01
  • 2021-07-25
  • 2018-02-23
  • 2018-07-06
  • 1970-01-01
  • 1970-01-01
  • 2019-05-22
  • 1970-01-01
相关资源
最近更新 更多