首页 文章

为什么C中的double会打印比C更少的十进制数字?

提问于
浏览
63

我在C中有这个代码,我已经宣布0.1为double .

#include <stdio.h> 
int main() {
    double a = 0.1;

    printf("a is %0.56f\n", a);
    return 0;
}

这就是它打印的内容, a is 0.10000000000000001000000000000000000000000000000000000000

C中的代码相同,

#include <iostream>
using namespace std;
int main() {
    double a = 0.1;

    printf("a is %0.56f\n", a);
    return 0;
}

这就是它的印刷品, a is 0.1000000000000000055511151231257827021181583404541015625

有什么不同?当我读到两个都被分配8个字节? C如何在小数位上打印更多数字?

另外,它怎么能到小数点55位? IEEE 754浮点只有52位的小数,我们可以得到15位十进制数的精度 . 它以二进制形式存储 . 为什么它的十进制解释存储更多?

2 回答

  • 10

    使用MinGW g(和gcc)7.3.0,您的结果将被准确复制 .

    这是一个非常奇怪的未定义行为案例 .

    未定义的行为是由于使用 printf 而没有包含适当的 Headers ,¹违反“应该”
    C++17 §20.5.2.2

    “翻译单位应仅在任何声明或定义之外包括 Headers ,并且应在该翻译单元的第一次参考之前词汇表中包含该 Headers ,以及该 Headers 中声明的任何实体 . 无需诊断 .

    在C代码更改 <iostream><stdio.h> ,以获得有效的C代码,并获得与C程序相同的结果 .


    为什么C代码甚至可以编译?

    好吧,与C不同,在C中,允许标准库头在任何其他头中拖动 . 显然,在 printf 的某些声明中, <iostream> Headers 拖拽 . 只是不完全正确 .

    详细信息:使用MinGW g 7.3.0时, printf 的声明/定义取决于宏符号 __USE_MINGW_ANSI_STDIO . 默认只是 <stdio.h> 声明 printf . 但是当 __USE_MINGW_ANSI_STDIO 被定义为逻辑真时, <stdio.h> 提供了 printf 的覆盖定义,即 __mingw_vprintf . 当发生这种情况时, <cstdio> 标头在包含 <stdio.h> 之前定义(通过间接包含) __USE_MINGW_ANSI_STDIO .

    <_mingw.h> ,"Note that we enable it also for _GNU_SOURCE in C++, but not for C case."中有评论 .

    在C中,使用此编译器的相关版本,包含 <stdio.h> 和使用 printf ,或包括 <cstdio> ,说 using std::printf; 和使用 printf 之间实际上存在差异 .


    关于

    “另外,它怎么能到小数点55位? IEEE 754浮点只有52位的小数,我们可以得到15位十进制数的精度 . 它以二进制形式存储 . 为什么它的十进制解释存储更多?

    ......只是十进制表示更长 . 超出内部表示精度的数字,对于64位IEEE 754约为15位,基本上是垃圾,但它们可用于精确地重构原始位 . 在某些时候,它们将变为全零,并且达到C程序输出中最后一位的那一点 .


    1感谢Dietrich Epp发现标准报价 .

  • 80

    在我看来,两种情况都打印56位小数,所以问题在技术上是基于一个有缺陷的前提 .

    我也看到两个数字在52位精度范围内等于 0.1 ,因此两者都是正确的 .

    这导致你的最终问题,"How come its decimal interpretation stores more?" . 它不存储更多小数 . double 不存储任何小数 . 它存储位 . 生成小数 .

相关问题