提问人:xiver77 提问时间:4/25/2022 最后编辑:xiver77 更新时间:4/27/2022 访问量:922
浮点文字中要尽可能正确地表示值的最小有效十进制位数是多少?
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)
但是,似乎需要 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 用于讨论)。9
9
8388609.499
用有效十进制数字表示的是 。此数字转换为的值为 。另一方面,用或更多数字表示的数字将始终保留原始值,并且该数字转换为具有值。9
8388609.50
float
8388610
10
float
8388609
您可以看到需要的不仅仅是数字才能最准确地转换为 。有无限多这样的数字,非常接近二进制浮点格式的两个可表示值的半点。8388609.499
9
float
答:
我认为您正在寻找常量。C 标准提供了关于它们如何计算的小解释和公式(N2176 C17 草案):*_DECIMAL_DIG
5.2.4.2.2 浮动类型的特征<float.h>
以下列表中给出的值应替换为常量表达式,这些常量表达式的实现定义值的大小(绝对值)大于或等于所示值,其中 相同的标志:
...
十进制位数 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 = 2
p = FLT_MANT_DIG = 24
FLT_DECIMAL_DIG = ⌈1 + 24 log10 2⌉ = 9
⌈x⌉=ceil(x)
评论
6
9
x
float
x
FLT_DECIMAL_DIG
浮点文字中要尽可能正确地表示值的最小有效十进制位数是多少?
C 标准不能保证浮点文字中任意数量的十进制数字将生成以浮点格式实际表示的最接近的值。在讨论浮点文字时,C 2018 6.4.4.2 3 说:
...对于十进制浮点常数,...结果是最接近的可表示值,或者是紧邻最接近的可表示值的较大或较小的可表示值,以实现定义的方式选择......
为了提高质量,C 实现应正确地将浮点文字四舍五入到最接近的可表示值,并带有偶数低位的选项。在这种情况下,中定义的 、 和 值提供的数字数始终足以唯一标识可表示值。FLT_DECIMAL_DIG
DBL_DECIMAL_DIG
LDBL_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 格式不会出现这种情况,在实践中可以忽略不计。
评论
9
8388609.4999...f
9
f
double
float
n
n
8388609.5
8388610
8388609
n + 7
float
float
float
8388609
双倍甚至任意精度呢,有没有一个简单的公式来推导出所需的位数>
摘自 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 十进制数字。float
p
n
float
float
例:
考虑 2 个相邻值float
10.000009_5367431640625
10.000010_49041748046875
两者都转换为 8 位有效数字十进制文本。8 是不够的。"10.000010"
9 总是足够的,因为我们不需要超过 167,772,160 来区分 16,777,216 个值。float
OP 还询问了 .(为了简单起见,我们只考虑一下。8388609.499
float
该值几乎是 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.499
8388608.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 个可能的 .float
FLT_TRUE_MIN
0.0f
float
为了避免我们越过精神错乱的悬崖,IEEE-758已经解决了这个问题。
翻译(编译器)必须检查以符合该规范(不一定是 C 规范)的有效十进制数字的数量要有限得多,即使额外的数字可以转换为另一个 FP 值。
IIRC,它实际上是.因此,对于一个普通的,只要9+3个有效十进制数字就可以被检查。FLT_DECIMAL_DIG + 3
float
[编辑]
只有所需的十进制位数加上 3 个支持的最大二进制格式才能保证正确的四舍五入。
*1C 不定义:浮点文字,但定义浮点常量,因此使用该术语。
评论
FLT_DECIMAL_DIG + 3
评论
float
double
float
float
float
%a