xiv*_*r77 7 c floating-point precision ieee-754 numerical-methods
例如,使用 IEEE-754 32 位二进制浮点数,让我们表示 的值1 / 3。它无法精确完成,但0x3eaaaaab会产生最接近 的值1 / 3。您可能希望以十进制形式写入值,并让编译器将十进制文字转换为二进制浮点数。
0.333333f -> 0x3eaaaa9f (0.333332986)\n0.3333333f -> 0x3eaaaaaa (0.333333313)\n0.33333333f -> 0x3eaaaaab (0.333333343)\n0.333333333f -> 0x3eaaaaab (0.333333343)\nRun Code Online (Sandbox Code Playgroud)\n您可以看到 8 位(有效)十进制数字足以表示尽可能正确的值(最接近实际值)。
\n我用 \xcf\x80 和 e(自然对数的底数)进行了测试,两者都需要 8 位十进制数字才能最正确。
\n3.14159f -> 0x40490fd0 (3.14159012)\n3.141593f -> 0x40490fdc (3.14159298)\n3.1415927f -> 0x40490fdb (3.14159274)\n3.14159265f -> 0x40490fdb (3.14159274)\n\n2.71828f -> 0x402df84d (2.71828008)\n2.718282f -> 0x402df855 (2.71828198)\n2.7182818f -> 0x402df854 (2.71828175)\n2.71828183f -> 0x402df854 (2.71828175)\nRun Code Online (Sandbox Code Playgroud)\n不过,\xe2\x88\x9a2似乎需要 9 位数字。
1.41421f -> 0x3fb504d5 (1.41420996)\n1.414214f -> 0x3fb504f7 (1.41421402)\n1.4142136f -> 0x3fb504f4 (1.41421366)\n1.41421356f -> 0x3fb504f3 (1.41421354)\n1.414213562f -> 0x3fb504f3 (1.41421354)\nRun Code Online (Sandbox Code Playgroud)\nhttps://godbolt.org/z/W5vEcs695
\n从这些结果来看,具有 9 个有效数字的十进制浮点文字可能足以产生最正确的 32 位二进制浮点值,并且在实践中,像 12~15 位这样的数字肯定可以工作如果存储额外数字的空间并不重要。
\n但我对其背后的数学感兴趣。在这种情况下,如何确定 9 位数字就足够了?甚至double任意精度呢,是否有一个简单的公式来导出所需的位数?
当前的答案和评论中的链接确认9数字对于大多数情况来说就足够了,但我发现了一个9数字不够的反例。事实上,十进制格式的无限精度需要始终正确转换(四舍五入到最接近的值)为某种二进制浮点格式(用于讨论的 IEEE-754 二进制 32 浮点数)。
8388609.499用9有效十进制数字表示的是8388609.50。该数字转换为float的值为8388610。另一方面,用10或多位数字表示的数字将始终保留原始值,并且该数字转换为float具有值8388609。
您可以看到8388609.499需要的不仅仅是9数字才能最准确地转换为float. 有无限多个这样的数字,它们非常接近二进制浮点格式中两个可表示值的半点。
我认为你正在寻找*_DECIMAL_DIG常数。C 标准提供了关于如何计算它们的小解释和公式(N2176 C17 草案):
\n\n5.2.4.2.2 浮点类型的特性 <float.h>
\n\n
\n- \n
以下列表中给出的值应替换为常量表达式,其实现定义的值大于或等于所示值(绝对值),并带有\n相同的符号:
\n...
\n\n
\n- \n
小数位数n,这样任何具有p基数b位的浮点数都可以四舍五入为具有n 位小数位的浮点数,并且可以在不更改值的情况下再次舍入,
\nRun Code Online (Sandbox Code Playgroud)\np log10 b if b is a power of 10\n\xe2\x8c\x881 + p log10 b\xe2\x8c\x89 otherwise\n\n\nFLT_DECIMAL_DIG 6\nDBL_DECIMAL_DIG 10\nLDBL_DECIMAL_DIG 10\n
对于 IEEE-754 32 位浮点数b = FLT_RADIX = 2和p = FLT_MANT_DIG = 24,结果为FLT_DECIMAL_DIG = \xe2\x8c\x881 + 24 log10 2\xe2\x8c\x89 = 9。( \xe2\x8c\x88x\xe2\x8c\x89=ceil(x)) 是取整函数:结果向上取整)
\n\n对于双精度甚至任意精度,是否有一个简单的公式来导出所需的位数?>
\n
从 C17 \xc2\xa7 5.2.4.2.2 11FLT_DECIMAL_DIG, DBL_DECIMAL_DIG, LDBL_DECIMAL_DIG
\n\n小数位数n,这样任何具有p基数b位的浮点数都可以四舍五入为具有n 位小数位的浮点数,并且可以在不更改值的情况下再次舍入,
\n
p max log 10 b: 如果b是 10 的幂
\n1 + p max log 10 b: 否则
\n\n但我对其背后的数学感兴趣。在这种情况下,如何确定 9 位数字就足够了?
\n
每个二进制浮点范围(如 [1.0 ... 2.0)、[128.0 ... 256.0)、[0.125 ... 0.5)] 包含 2 个均匀分布的p - 1值。例如float,对于 ,p = 24。
具有指数表示法有效数字的十进制文本的每个范围,n例如 [1.0 ... 9.999...)、[100.0f ... 999.999...)、[0.001 ... 0.00999...) 包含 10 n - 1 个值均匀分布。
示例: common float:
\n当p24 具有 2 24 个组合时,n必须至少有8 个组合才能形成 16,777,216 个组合才能明确往返float于十进制文本到float。由于上述两个小数范围的端点可能很好地存在于该组 2 24内,因此较大的小数值间隔得更远。这需要+1 位小数。
例子:
\n考虑2个相邻的float值
10.000009_5367431640625\n10.000010_49041748046875\nRun Code Online (Sandbox Code Playgroud)\n两者都转换为 8 位有效数字的十进制文本"10.000010"。8个还不够。
9 总是足够的,因为我们不需要超过 167,772,160 来区分 16,777,216float 值。
OP 还询问了8388609.499. (为了简单起见,我们只考虑一下float。)
该值几乎是两个值的中间值float。
8388609.0f // Nearest lower float value\n8388609.499 // OP\'s constant as code\n8388610.0f // Nearest upper float value\nRun Code Online (Sandbox Code Playgroud)\nOP 报告:“您可以看到 8388609.499 需要 9 位以上的数字才能最准确地转换为浮点数。”
\n让我们回顾一下标题“浮点文字*1中有效小数位数的最小数量是多少,以尽可能正确地表示值?”
\n这个新问题部分强调所讨论的值是源代码的值8388609.499,而不是它在发出的代码中变成的浮点常量:8388608.0f。
如果我们将该值视为浮点常量的值,则最多只需要 9 位有效小数位即可定义浮点常量 8388608.0f。8388608.49,源代码就足够了。
但是要根据某个数字作为代码获得最接近的浮点常数,确实可能需要很多位。
\n考虑典型的最小值float,FLT_TRUE_MIN其精确的十进制值为 :
0.00000000000000000000000000000000000000000000140129846432481707092372958328991613128026194187651577175706828388979108268586060148663818836212158203125\nRun Code Online (Sandbox Code Playgroud)\n0.0 和 0.0 之间的一半是 0.000..(大约 39 个零)..0007006..(大约 100 个数字)..15625。
\n如果最后一位数字是 6 或 4,最接近的数字分别float是FLT_TRUE_MIN或0.0f。所以现在我们遇到这样一种情况:“需要”109 个有效数字才能在 2 个可能的值之间进行选择float。
为了避免我们陷入疯狂的悬崖,IEEE-758 已经解决了这个问题。
\n翻译(编译器)必须检查以符合该规范(不一定是 C 规范)的有效十进制数字的数量要有限得多,即使额外的数字可以转换为另一个 FP 值。
\nIIRC,已生效FLT_DECIMAL_DIG + 3。因此,对于常见的float,可以检查最少 9 + 3 个有效小数位。
[编辑]
\n仅保证所需的十进制位数加上 3(支持的最大二进制格式)正确舍入。
\n*1 C 没有定义:浮点文字,但定义了浮点常量,因此使用该术语。
\n