【问题标题】:printing very large floating point numbers打印非常大的浮点数
【发布时间】:2012-06-10 17:00:08
【问题描述】:
#include <stdio.h>
#include <float.h>

int main()
{
    printf("%f\n", FLT_MAX);
}

来自 GNU 的输出:

340282346638528859811704183484516925440.000000

Visual Studio 的输出:

340282346638528860000000000000000000000.000000

C 和 C++ 标准是否允许这两种结果?还是他们要求特定的结果?

注意FLT_MAX = 2^128-2^104 = 340282346638528859811704183484516925440

【问题讨论】:

  • C 和 C++ 标准甚至不强制要求特定的浮点表示。所以我倾向于认为他们不能强制要求特定的结果。
  • @Mysticial 好吧,他们可能仍然普遍要求“必须打印出所代表的确切值”或类似的东西。
  • 而 Unix pwns Windows。再一次。
  • @H2CO3,您如何得出 GNU 优于 Windows 的结论?我更喜欢微软的结果,因为它正确地暗示了分辨率是有限的。
  • 查看我的两篇文章 exploringbinary.com/…exploringbinary.com/… 。您将看到位数如何因语言和实现而异。

标签: c++ c floating-point standards printf


【解决方案1】:

我认为 C99 标准的相关部分是 7.19.6.1 p.13 中的“推荐实践”:

对于eEfFgG 转换,如果有效小数位数最多为 DECIMAL_DIG,则结果应正确舍入。如果数 有效十进制数字大于DECIMAL_DIG,但源值恰好是 可以用DECIMAL_DIG 数字表示,那么结果应该是精确的 带有尾随零的表示。否则,源值以两个为界 相邻的十进制字符串 L U,都具有DECIMAL_DIG 有效数字;价值 得到的十进制字符串 D 应该满足 L D U,额外的规定是 对于当前的舍入方向,错误应该有一个正确的符号。

我的印象是,这允许在这种情况下打印的内容有一些余地;所以我的结论是VS和GCC在这里都是合规的。

【讨论】:

  • @ColeJohnson:没有比这更精确的了。 GNU 欺骗你以为它有,但它的结果比 MSVC 的还要糟糕。浮点表示间隔,而不是数字。所以 340282346638528859811704183484516925440、340282346638528859811704183484516925450 和 340282346638528860000000000000000000060 都是相同的浮点值。 A better floating point formatting rountine is actually the one that gives you the shortest possible representation.
  • @ybungalobill:浮点不代表区间。它表示可能是精确的或四舍五入的结果,具体取决于您的使用情况。使用浮点进行区间运算实际上非常困难(并且要么快速扩展区间,要么需要不断改变舍入方向和尊重舍入模式的实现)。
  • @FredOverflow:GNU 结果是正确的值,但两者似乎都符合标准中的要求和推荐做法。
  • 它们不是无意义的数字。问题是值“1e-20”已经四舍五入了。 GNU libc(不是 GCC;GCC 是编译器,与 printf 行为无关)版本打印浮点表达式的确切值。如果您的意思是表示数字 1e-20 的值,那么最后一位数字可能对 没有意义,但 printf 无法知道您打算作为 1e-20 的近似值而不是浮点表达式的精确值。 MSVCRT 行为在任何方面都没有好处;这只是懒惰。
  • 按照 IEEE 浮点算术标准 754-2008 的规定,浮点数据仅表示特定数字(和 NaN),而不是区间。根据第 3.3 节,表示的数据为 +0; -0; (-1)**s * b**e * m 形式的数字,其中 s 是 0 或 1,b、e 和 m 是特定浮点类型的合适的底数、指数和有效数; +infinity、-infinity、安静的 NaN 和信号 Nan。浮点运算的结果由操作数表示的单个数字决定,而不是由它们周围的间隔决定。
【解决方案2】:

两者都被 C 标准允许(C++ 只是导入了 C 标准)

来自第 5.2.4.2.2 节第 10 部分中的draft version

以下列表中给出的值应替换为常量表达式 大于或等于所示值的实现定义值:
— 最大可表示的有限浮点数,(1 − b −p)b emax

FLT_MAX 1E+37

Visual C++ 2012 有

#define FLT_MAX         3.402823466e+38F        /* max value */

【讨论】:

  • 实际上,FLT_MAX 的值在任何使用 IEEE-754 浮点的平台上都是相同的。所以这不是这里的问题。
  • @OliCharlesworth:好吧,也许“有缺陷”太强了。它正在调用未定义的结果。
【解决方案3】:

代码本身存在缺陷,因为它使用%f 的值大于floatdouble 中的意义。通过这样做,您要求了解在转换为十进制时产生的无意义的保护位或其他浮点噪声的“幕后”。

显然,您不应该期望在本田和丰田制造发动机后产生的金属屑有任何一致性。不要介意对这种一致性的任何合理期望。

显示此类数字的正确方法是使用“科学”格式之一,例如 %g,只要不过度指定精度即可。在 IEEE-754 实现中,float 有 7 个十进制数字,double 有 15-16 个有效数字,long double 有 19 个有效数字,__float128 有 34 个有效数字。因此,对于您给出的示例,%.15g 将是正确的,假设它是在 IEEE-754 实现上。

【讨论】:

  • 使用%f 没有“缺陷”。问题是询问标准允许“幕后”的内容。
  • @OliCharlesworth:好吧,也许“有缺陷”太强了。它正在调用未定义的结果。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-05-16
  • 2015-02-02
  • 1970-01-01
  • 2011-07-11
  • 1970-01-01
  • 1970-01-01
  • 2022-09-23
相关资源
最近更新 更多