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

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

提问人:xiver77 提问时间:4/25/2022 最后编辑:xiver77 更新时间:4/27/2022 访问量:922

问:

例如,使用 IEEE-754 32 位二进制浮点数,让我们表示 的值。它不能完全完成,但会产生最接近 的值。您可能希望以十进制形式写入该值,并让编译器将十进制文本转换为二进制浮点数。1 / 30x3eaaaaab1 / 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)

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

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 位数字肯定会起作用。

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


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

8388609.499用有效十进制数字表示的是 。此数字转换为的值为 。另一方面,用或更多数字表示的数字将始终保留原始值,并且该数字转换为具有值。98388609.50float838861010float8388609

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

C 浮点 精度 数值方法 IEEE-754

评论

1赞 Weather Vane 4/25/2022
如果将有效位数乘以 log(10)2,即 0.30103,则得到可以表示的有效十进制位数。但准确的小数位数取决于值的整数部分。因此,对于精度约为 7 位的值,任何值> 9999999其小数位的精度为零。如果您想要上述 12~15 位精度,请使用 .floatdouble
1赞 Weather Vane 4/25/2022
非理性或重复性 vlaue 的最正确值将由您可用的最大类型持有。永远不要使用 ,永远(它不再是 1980 年了),除非你有很好的理由需要使用 .floatfloat
1赞 Weather Vane 4/25/2022
正如我所说,您应该使用最多的数字。对于您所做的每一次计算,在一连串计算中,您都会失去更多的准确性。无论值是什么,有些值都不是“比其他的更精确”,除非是你使用的最后一个十进制数字。没有必要挑选你使用的地方数量:使用最可用的地方。
1赞 Bob__ 4/25/2022
请注意,在 IEEE 754 32 位的尾数中,有一个隐式的第 24 位设置为 1。如果您想往返,请使用。float%a
3赞 kvantour 4/25/2022
布鲁斯·道森(Bruce Dawson)在他的技术博客Random Ascii上写了一篇关于此的精彩文章以及更多内容。我建议你看看它以及他所做的许多其他出色的调查。浮点精度 – 从 0 到 100+ 位

答:

8赞 user694733 4/25/2022 #1

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

5.2.4.2.2 浮动类型的特征<float.h>

  1. 以下列表中给出的值应替换为常量表达式,这些常量表达式的实现定义值的大小(绝对值)大于或等于所示值,其中 相同的标志:

    ...

    • 十进制位数 n,使得任何具有 p 基数 b 位数的浮点数都可以四舍五入为具有 n 个十进制数字的浮点数,然后再次返回,而无需更改值,

      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 位浮点数 和 ,结果为 。() is ceiling 函数:四舍五入结果)b = FLT_RADIX = 2p = FLT_MANT_DIG = 24FLT_DECIMAL_DIG = ⌈1 + 24 log10 2⌉ = 9⌈x⌉=ceil(x)

评论

0赞 xiver77 4/25/2022
这是针对浮点数的,但我想要的常数至少从测试中很明显。69
1赞 user694733 4/25/2022
@xiver77 见上文;这些是最小值。您的计算机应报告 9。
0赞 xiver77 4/25/2022
哦,是的,现在是 9 岁。有趣!我去看看!
1赞 user694733 4/25/2022
@xiver77 是的,我同意它写得很混乱。我希望我在答案末尾添加的计算对未来的读者有所帮助。
1赞 user694733 4/25/2022
@xiver77 When 在 中不完全可表示时,根据舍入规则将其转换为 2 个最接近的值中的任何一个。由于数据丢失,因此无法恢复。转换后,C 标准承诺,通过使用,您可以将值转换为十进制数并返回,而不会造成进一步的损失。您可以通过更精确地使用数据类型来改善这种情况,但即便如此,总会有一些数字在二进制和十进制之间的转换需要无限的精度。xfloatxFLT_DECIMAL_DIG
3赞 Eric Postpischil 4/25/2022 #2

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

C 标准不能保证浮点文字中任意数量的十进制数字将生成以浮点格式实际表示的最接近的值。在讨论浮点文字时,C 2018 6.4.4.2 3 说:

...对于十进制浮点常数,...结果是最接近的可表示值,或者是紧邻最接近的可表示值的较大或较小的可表示值,以实现定义的方式选择......

为了提高质量,C 实现应正确地将浮点文字四舍五入到最接近的可表示值,并带有偶数低位的选项。在这种情况下,中定义的 、 和 值提供的数字数始终足以唯一标识可表示值。FLT_DECIMAL_DIGDBL_DECIMAL_DIGLDBL_DECIMAL_DIG<float.h>

在这种情况下,如何确定 9 位数字就足够了?

您需要在编译器文档中实现此效果的语句,例如它为浮点文本提供正确的舍入,以及它使用 IEEE-754 binary32(也称为“单精度”)作为(或一些其他格式,只需要 9 位有效数字来唯一标识所有可表示值)。float

双倍甚至任意精度呢,有没有一个简单的公式来推导出所需的位数?

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

评论

0赞 xiver77 4/25/2022
有些数字需要超过数字才能正确转换为 IEEE 二进制32。 S的数量很多,但数量有限,就是一个例子(需要避免从到的舍入误差)。如果小数部分有十进制数字,则小数部分中少于数字的任何十进制表示都将四舍五入到,然后四舍五入为 ,而正确四舍五入的结果是 。此数字至少需要十进制数字才能正确转换为 binary32。98388609.4999...f9fdoublefloatnn8388609.583886108388609n + 7
0赞 Eric Postpischil 4/25/2022
@xiver77:不清楚你的意思。你讨论了某种双舍入,显然是来自某个数字 8388609.4999......9 到一些较小的十进制数字,然后到 .您不需要超过 9 位数字即可获得所需的 888609.4999 值...9,因为您可以使用 来获取该值,它只有 7 位数字。你在问其他问题......floatfloatfloat8388609
0赞 Eric Postpischil 4/25/2022
...也许是这样的:对于浮点范围内的任何实数 x,将 x 四舍五入为具有 d 位有效数字的十进制数字 D,然后将 D 四舍五入为浮点格式会产生与将 x 四舍五入为浮点格式相同的结果的最小数字 d 是多少? 这个问题的答案是没有这样的有限数d。这是制表者的困境;在两个可表示的数字之间总是有一个四舍五入点,其中四舍五入到一个与另一个的决定会发生变化,并且存在任意接近该点的数字。
0赞 xiver77 4/25/2022
请参阅OP底部添加的句子。我希望这能澄清。
6赞 chux - Reinstate Monica 4/26/2022 #3

双倍甚至任意精度呢,有没有一个简单的公式来推导出所需的位数>

摘自 C17 § 5.2.4.2.2 11FLT_DECIMAL_DIG, DBL_DECIMAL_DIG, LDBL_DECIMAL_DIG

十进制位数 n,使得任何具有 p 基数 b 位数的浮点数都可以四舍五入为具有 n 个十进制数字的浮点数,然后再次返回,而无需更改值,

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


但我对它背后的数学很感兴趣。在这种情况下,如何确定 9 位数字就足够了?

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

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

示例:普通:
当 24 与 224 组合时,必须至少为 8 才能形成 16,777,216 个组合,才能明显往返到十进制文本。由于上述两个小数范围的端点可能存在于 224 的集合中,因此较大的小数值之间的距离更远。这需要一个 +1 十进制数字。
floatpnfloatfloat

例:

考虑 2 个相邻值float

10.000009_5367431640625
10.000010_49041748046875

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

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


OP 还询问了 .(为了简单起见,我们只考虑一下。8388609.499float

该值几乎是 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 中有效十进制位数的最小数量是多少,以尽可能正确地表示该值

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

如果我们认为该值是浮点常数的值,则最多只需要 9 位有效十进制数字即可定义浮点常数8388608.49,因为源代码就足够了。8388608.0f

但是,要根据某个数字作为代码获得最接近的浮点常数,是的确实可能需要许多数字。

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

0.00000000000000000000000000000000000000000000140129846432481707092372958328991613128026194187651577175706828388979108268586060148663818836212158203125

介于 0.0 和 0.0 之间的是 0.000。(~39 多个零)..0007006..(~ 100 多位)..15625.

如果最后一个数字是 6 或 4,则最接近的将是 或 分别。所以现在我们有一个情况,其中“需要”109 位有效数字来在 2 个可能的 .floatFLT_TRUE_MIN0.0ffloat

为了避免我们越过精神错乱的悬崖,IEEE-758已经解决了这个问题。

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

IIRC,它实际上是.因此,对于一个普通的,只要9+3个有效十进制数字就可以被检查。FLT_DECIMAL_DIG + 3float

[编辑]

只有所需的十进制位数加上 3 个支持的最大二进制格式才能保证正确的四舍五入。


*1C 不定义:浮点文字,但定义浮点常量,因此使用该术语。

评论

0赞 xiver77 4/26/2022
感谢您的明确解释。用可能的组合数量来思考它,使问题更容易识别。您能否从最近的编辑中看一下 OP 的底部?
0赞 xiver77 4/26/2022
我接受你的回答,因为你已经解释的比我要求的要多得多,但我对为什么“编译器必须检查的有效十进制数字的数量”非常感兴趣。请随时解释这部分并留下回复,以便我得到一个ping。FLT_DECIMAL_DIG + 3
0赞 xiver77 4/26/2022
GCC 和 Clang 似乎在实践中都检查了 1000 多个十进制数字(godbolt.org/z/e9dz6sjf4),但规范定义的内容对我来说仍然很有趣。
0赞 chux - Reinstate Monica 4/27/2022
@xiver77 添加了引用。也许稍后添加 754 规格报价 - 现在没有电子设备。