为什么C语言中的double打印比C ++少打印十进制数字?

时间:2018-10-05 07:20:05

标签: c++ c floating-point numbers precision

我在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个十进制数字的精度。它以二进制形式存储。十进制解释如何存储更多内容?

2 个答案:

答案 0 :(得分:81)

使用MinGW g ++(和gcc)7.3.3,您的结果可以精确再现。

这是未定义行为的非常奇怪的情况。

未定义行为是由于使用printf而未包含适当的标头,“违反了”中的“应”

C ++ 17§20.5.2.2
  

翻译单元应仅在任何声明或定义之外包括标头,并应包括   在该翻译单元中第一次引用该标头中声明的任何实体之前,按字头顺序标头。   无需诊断。

在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不存储任何小数。它存储位。小数生成。