Kei*_*son 36
IEEE double有53个有效位(即DBL_MANT_DIGin 的值<cfloat>).这是大约15.95十进制数字(log10(2 53)); 实现设置DBL_DIG为15,而不是16,因为它必须向下舍入.因此,你几乎有一个额外的十进制数字精度(超出隐含的数字DBL_DIG==15).
该nextafter()函数计算给定数字的最近可表示数字; 它可以用来显示给定数字的精确程度.
这个程序:
#include <cstdio>
#include <cfloat>
#include <cmath>
int main() {
double x = 1.0/7.0;
printf("FLT_RADIX = %d\n", FLT_RADIX);
printf("DBL_DIG = %d\n", DBL_DIG);
printf("DBL_MANT_DIG = %d\n", DBL_MANT_DIG);
printf("%.17g\n%.17g\n%.17g\n", nextafter(x, 0.0), x, nextafter(x, 1.0));
}
Run Code Online (Sandbox Code Playgroud)
在我的系统上给我这个输出:
FLT_RADIX = 2
DBL_DIG = 15
DBL_MANT_DIG = 53
0.14285714285714282
0.14285714285714285
0.14285714285714288
Run Code Online (Sandbox Code Playgroud)
(可以替换%.17g,比方说,%.64g看到更多的数字,其中没有一个是显著.)
如您所见,最后显示的十进制数值随每个连续值的变化而变化3.1.0/7.0(5)的最后一个显示数字恰好与数学值匹配的事实在很大程度上是巧合的; 这是一个幸运的猜测.并且正确的舍入数字6不是5.替换1.0/7.0为1.0/3.0提供此输出:
FLT_RADIX = 2
DBL_DIG = 15
DBL_MANT_DIG = 53
0.33333333333333326
0.33333333333333331
0.33333333333333337
Run Code Online (Sandbox Code Playgroud)
如您所料,它显示大约16个十进制数字的精度.
tru*_*ity 16
它实际上是53个二进制位,转换为15个稳定的小数位,这意味着如果你用一个带有15个小数位的数字开始,将它转换为a double,然后将其转double回到15个小数位,你将获得相同的号码.要唯一地表示double你需要17个小数位(意味着每个数字有17个小数位,有一个唯一的最接近的double)这就是为什么17个位置出现,但并非所有17位小数都映射到不同的double值(如示例中所示)在其他答案).
Joh*_*eek 12
浮点数的十进制表示有点奇怪.如果你有一个带有15个小数位的数字并将其转换为a double,然后用正好15个小数位打印出来,你应该得到相同的数字.另一方面,如果您打印出double15个小数位的任意值并将其转换回a double,则不一定会得到相同的值 - 您需要17位小数.并且15和17小数都不足以准确显示任意的精确十进制等值double.通常,您需要超过100个小数位才能准确地执行此操作.
有关双精度的详细信息,请参阅Wikipedia页面以及有关浮点精度的本文.
双精确地保存53个二进制数字,即〜15.9545898十进制数字.调试器可以显示尽可能多的数字,以便更准确地处理二进制值.或者它可能需要更少的数字和二进制,例如0.1在基数10中取1位数,但在基数2中取无限数.
这很奇怪,所以我将展示一个极端的例子.如果我们创建一个超级简单的浮点值,它只保留3个精度的二进制数字,并且没有尾数或符号(因此范围是0-0.875),我们的选项是:
binary - decimal
000 - 0.000
001 - 0.125
010 - 0.250
011 - 0.375
100 - 0.500
101 - 0.625
110 - 0.750
111 - 0.875
Run Code Online (Sandbox Code Playgroud)
但是如果你做数字,这种格式只能精确到0.903089987十进制数字.甚至1位数都不准确.很容易看出,因为没有以0.4??nor 开头的值0.9??,并且还没有显示完整的精度,我们需要3个十进制数字.
tl; dr:调试器向您显示浮点变量的值到某个任意精度(在您的情况下为19位),这不一定与浮点格式的精度相关(在您的情况下为17位).
IEEE 754 浮点数以二进制形式完成。从给定的位数到给定的十进制数字没有精确的转换。3 位可以保存 0 到 7 的值,4 位可以保存 0 到 15 的值。0 到 9 的值大约需要3.5 位,但这也不准确。
IEEE 754 双精度数占用 64 位。其中,52 位专用于有效数(其余为符号位和指数)。由于尾数(通常)归一化,有一个隐含的53次位。
现在,给定 53 位和大约每位 3.5 位,简单的除法给我们 15.1429 位的精度。但请记住,每个十进制数字 3.5 位只是一个近似值,而不是一个完全准确的答案。
许多(大多数?)调试器实际上查看整个寄存器的内容。在 x86 上,这实际上是一个 80 位数字。x86 浮点单元通常会被调整为执行 64 位精度的计算——但在内部,它实际上使用了几个“保护位”,这基本上意味着它在内部以一些额外的精度进行计算,所以它可以正确舍入最后一个。当调试器查看整个寄存器时,它通常会找到至少一个相当准确的额外数字——尽管由于该数字没有任何保护位,它可能无法正确舍入。
这是因为它是从二进制表示形式转换而来的。仅仅因为它打印了所有这些十进制数字并不意味着它可以以该精度表示所有十进制值。以 Python 为例:
>>> 0.14285714285714285
0.14285714285714285
>>> 0.14285714285714286
0.14285714285714285
Run Code Online (Sandbox Code Playgroud)
请注意我如何更改最后一位数字,但它仍然打印出相同的数字。
| 归档时间: |
|
| 查看次数: |
111542 次 |
| 最近记录: |