浮点文字中有效十进制数字的最小位数是多少,以尽可能正确地表示该值?

What is the minimum number of significant decimal digits in a floating point literal to represent the value as correct as possible?

例如,使用 IEEE-754 32 位二进制浮点数,我们来表示 1 / 3 的值。它不能完全完成,但 0x3eaaaaab 产生最接近 1 / 3 的值。您可能希望以十进制形式写入值,并让编译器将十进制文字转换为二进制浮点数。

0.333333f    -> 0x3eaaaa9f (0.333332986)
0.3333333f   -> 0x3eaaaaaa (0.333333313)
0.33333333f  -> 0x3eaaaaab (0.333333343)
0.333333333f -> 0x3eaaaaab (0.333333343)

可以看到8位(有效的)十进制数字就足以表示尽可能正确的值(最接近实际值)。

我用 π 和 e(自然对数的底数)进行了测试,两者都需要 8 位小数才能最正确。

3.14159f    -> 0x40490fd0 (3.14159012)
3.141593f   -> 0x40490fdc (3.14159298)
3.1415927f  -> 0x40490fdb (3.14159274)
3.14159265f -> 0x40490fdb (3.14159274)

2.71828f    -> 0x402df84d (2.71828008)
2.718282f   -> 0x402df855 (2.71828198)
2.7182818f  -> 0x402df854 (2.71828175)
2.71828183f -> 0x402df854 (2.71828175)

但是,√2 似乎需要 9 位数字。

1.41421f     -> 0x3fb504d5 (1.41420996)
1.414214f    -> 0x3fb504f7 (1.41421402)
1.4142136f   -> 0x3fb504f4 (1.41421366)
1.41421356f  -> 0x3fb504f3 (1.41421354)
1.414213562f -> 0x3fb504f3 (1.41421354)

https://godbolt.org/z/W5vEcs695

从这些结果来看,具有 9 位有效数字的十进制浮点文字足以产生最正确的 32 位二进制浮点值可能是正确的,实际上 12~15 位数字就可以了确定 space 是否用于存储额外的数字并不重要。

但我对它背后的数学很感兴趣。在这种情况下,如何确定 9 位数字就足够了? double甚至任意精度,有没有简单的公式可以推导出所需的位数?


当前的答案和评论中的链接确认 9 数字足以满足 大多数 的情况,但我发现了一个反例 9 位数不够。事实上,十进制格式的无限精度需要始终正确转换(四舍五入到最接近的)某种二进制浮点格式(IEEE-754 binary32 floats 供讨论)。

8388609.4999位有效小数表示为8388609.50。此数字转换为 float 后的值为 8388610。另一方面,用10或更多位表示的数字将始终保留原始值,并且此数字转换为float具有值8388609

您可以看到 8388609.499 需要多于 9 位才能最准确地转换为 float。有无限多个这样的数字,非常接近二进制浮点格式中两个可表示值的半点。

我认为您正在寻找 *_DECIMAL_DIG 常量。 C 标准提供了关于如何计算它们的小解释和公式(N2176 C17 草案):

5.2.4.2.2 Characteristics of floating types <float.h>

  1. The values given in the following list shall be replaced by constant expressions with implementation-defined values that are greater or equal in magnitude (absolute value) to those shown, with the same sign:

    ...

    • number of decimal digits, n, such that any floating-point number with p radix b digits can be rounded to a floating-point number with n decimal digits and back again without change to the value,

      p log10 b        if b is a power of 10
      ⌈1 + p log10 b⌉  otherwise
      
      
      FLT_DECIMAL_DIG  6
      DBL_DECIMAL_DIG  10
      LDBL_DECIMAL_DIG 10
      

使用 IEEE-754 32 位浮点数 b = FLT_RADIX = 2p = FLT_MANT_DIG = 24,结果为 FLT_DECIMAL_DIG = ⌈1 + 24 log10 2⌉ = 9。 (⌈x⌉=ceil(x)) 是上限函数:向上舍入结果)

What is the minimum number of significant decimal digits in a floating point literal to represent the value as correct as possible?

C 标准无法保证 floating-point 文字中的任何小数位数都将产生最接近 floating-point 格式实际可表示的值。在讨论 floating-point 文字时,C 2018 6.4.4.2 3 说:

… For decimal floating constants, … the result is either the nearest representable value, or the larger or smaller representable value immediately adjacent to the nearest representable value, chosen in an implementation-defined manner…

为了质量,C 实现应该正确地将 floating-point 文字四舍五入到最接近的可表示值,并与偶数位较低的选项保持一致。在这种情况下,在 <float.h> 中定义的 FLT_DECIMAL_DIGDBL_DECIMAL_DIGLDBL_DECIMAL_DIG 值提供了始终足以唯一标识可表示值的位数。

How can one be sure that 9 digits is enough in this case?

您需要在编译器文档中声明这方面的效果,例如声明它为 floating-point 文字提供正确的舍入并且它使用 IEEE-754 binary32(a.k.a。“单精度” ) 对于 float(或其他一些只需要九个有效数字来唯一标识所有可表示值的格式)。

What about double or even arbitrary precision, is there a simple formula to derive the number of digits needed?

C 标准表示上面的常数计算为 p log10 b 如果b 是十的幂和 ceil(1 + p log10 b ) 否则,其中 p 是 floating-point 格式中的位数,而 b 是使用的基数格式。这些总是足够的,但后者并不总是必要的。如果指数范围是无限的,后者提供所需的位数;从某种意义上说,它的“1+”涵盖了 b 的幂如何与 10 的幂相互作用的所有可能余量。但是任何 floating-point 格式都有一个有限的指数范围,并且对于某些指数范围的选择, ceil(p log10 b) 就足够了,而不是 ceil(1 + p log10 b).这没有简单的公式。它不会出现在标准 IEEE-754 格式中,在实践中可以忽略。

What about double or even arbitrary precision, is there a simple formula to derive the number of digits needed?>

来自 C17 § 5.2.4.2.2 11 FLT_DECIMAL_DIG, DBL_DECIMAL_DIG, LDBL_DECIMAL_DIG

number of decimal digits, n, such that any floating-point number with p radix b digits can be rounded to a floating-point number with n decimal digits and back again without change to the value,

pmax log10 b:如果b是10的幂
1 + pmax log10 b:否则


But I'm interested in the math behind it. How can one be sure that 9 digits is enough in this case?

二进制浮点数的每个范围如 [1.0 ... 2.0), [128.0 ... 256.0), [0.125 ... 0.5) 包含 2p - 1值均匀分布。例如float,p = 24。

十进制文本的每个范围具有 n 指数表示法中的有效数字,例如 [1.0 ... 9.999...), [100.0f ... 999.999...), [0.001 ... 0.00999...) 包含 10n - 1 个均匀分布的值。

示例:常见 float
p 为 24 和 224 组合时,n 必须至少 8 才能形成 16,777,216 种组合以明确 round-trip float 将十进制文本转为 float。由于上面两个小数范围的 end-points 可能很好地存在于 224 的集合中,因此较大的小数值间隔得更远。这需要一个 +1 小数位。

示例:

考虑 2 个相邻的 float

10.000009_5367431640625
10.000010_49041748046875

两者都转换为 8 位有效数字的十进制文本 "10.000010"。 8个不够。

9 总是足够的,因为我们不需要超过 167,772,160 来区分 16,777,216 个 float 值。


OP 还询问 8388609.499。 (为简单起见,我们只考虑 float。)

该值几乎 half-way 介于 2 float 个值之间。

8388609.0f  // Nearest lower float value
8388609.499 // OP's constant as code
8388610.0f  // Nearest upper float value

OP 报告:“您可以看到 8388609.499 需要超过 9 位数字才能最准确地转换为浮点数。”

然后让我们回顾一下标题“浮点数中有效小数位数的最小位数是多少*1来表示 尽可能正确?"

这个新问题部分强调所讨论的 value 是源代码 8388609.499 的值,而不是它在发出的代码中变成的浮点常量:8388608.0f.

如果我们将视为浮点常量的值,则最多只需要9位有效的小数位定义 浮点常量 8388608.0f。 8388608.49,源代码就够了。

但是要根据一些数字获得最接近的 浮点常量 是的,确实需要很多数字。

考虑典型的最小值 floatFLT_TRUE_MIN 具有精确的十进制值:

0.00000000000000000000000000000000000000000000140129846432481707092372958328991613128026194187651577175706828388979108268586060148663818836212158203125

那个和 0.0 之间的一半是 0.000..(~39 个零)..0007006..(~100 个数字)..15625.

如果最后一位数字是 6 或 4,最接近的 float 将分别是 FLT_TRUE_MIN0.0f。所以现在我们有一个情况,在 2 个可能的 float.

之间“需要”109 个有效数字到 select

为了避免我们讨论 cliffs of insanity,IEEE-758 已经解决了这个问题。

翻译(编译器)必须检查以符合该规范(不一定是 C 规范)的有效十进制数字的数量要有限得多,即使额外的数字可以转换为另一个 FP 值。

IIRC,生效FLT_DECIMAL_DIG + 3。因此,对于一个常见的 float,可以检查少至 9 + 3 位有效的小数位。

[编辑]

correct rounding is only guaranteed for the number of decimal digits required plus 3 for the largest supported binary format.


*1 C 没有定义:浮点字面值,但是定义了浮点常量[=126] =],因此使用了该术语。