我在C中有这段代码,其中我将0.1声明为double。
for(auto it = m.begin(); it != m.end(); it++){
这是它打印的内容, clientBootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 1000);
C ++中的相同代码
#include <stdio.h>
int main() {
double a = 0.1;
printf("a is %0.56f\n", a);
return 0;
}
这是它打印的内容,a is 0.10000000000000001000000000000000000000000000000000000000
有什么区别?当我阅读时都分配了8个字节吗? C ++如何在小数点后显示更多数字?
还有,怎么能到小数点后55位呢? IEEE 754浮点数的小数位数只有52位,使用它可以得到15个十进制数字的精度。它以二进制形式存储。十进制解释如何存储更多内容?
答案 0 :(得分:81)
使用MinGW g ++(和gcc)7.3.3,您的结果可以精确再现。
这是未定义行为的非常奇怪的情况。
未定义行为是由于使用printf
而未包含适当的标头,“违反了”中的“应”
” 翻译单元应仅在任何声明或定义之外包括标头,并应包括 在该翻译单元中第一次引用该标头中声明的任何实体之前,按字头顺序标头。 无需诊断。
在C ++代码中,将<iostream>
更改为<stdio.h>
,以获得有效的C ++代码,您将获得与C程序相同的结果。
为什么C ++代码甚至可以编译?
与C不同,在C ++中,允许将标准库头拖入任何其他头中。显然,对于g ++,<iostream>
标头拖入了printf
的某些声明。只是不完全正确。
详细信息:在MinGW g ++ 7.3.0中,printf
的声明/定义取决于宏符号 __USE_MINGW_ANSI_STDIO
。缺省值为<stdio.h>
声明printf
。但是,当__USE_MINGW_ANSI_STDIO
被定义为逻辑真时,<stdio.h>
提供了printf
的重写定义,该定义调用__mingw_vprintf
。碰巧,<cstdio>
标头在包含__USE_MINGW_ANSI_STDIO
之前(通过间接包含)定义了<stdio.h>
。
<_mingw.h>
中有一条注释,“请注意,我们也为C ++中的_GNU_SOURCE启用了它,但没有为C情况启用它。”
在C ++中,使用此编译器的相关版本,包含<stdio.h>
和使用printf
或包含<cstdio>
,说using std::printf;
和使用之间实际上是有区别的printf
。
关于
” 另外,如何将其保留到小数点后55位? IEEE 754浮点数的小数位数只有52位,使用它可以得到15个十进制数字的精度。它以二进制形式存储。十进制解释如何存储更多内容?
...只是十进制的表示更长。这些数字超出内部表示的精度,对于64位IEEE 754,大约为15位,本质上是垃圾,但是可以用来精确地重构原始位。在某些时候它们将变为全零,并且C ++程序输出中的最后一位达到该点。
1 感谢Dietrich Epp查找该标准报价。
答案 1 :(得分:10)
在我看来,这两种情况都打印出56个十进制数字,所以从技术上来说,这个问题是基于有缺陷的前提的。
我还看到,两个数字在52位精度内都等于0.1
,所以两个都是正确的。
这导致您最后一个疑问,“它的十进制解释如何存储更多?”。它不会存储更多的小数。 double
不存储任何小数。它存储位。小数生成。