两次溢出?

Ano*_*mus 5 c++ overflow

我一直想知道万一double达到最大值,会发生什么,所以我决定编写以下代码:

#include <stdint.h>
#include <iostream>
#define UINT64_SIZE 18446744073709551615
int main() {
    std::uint64_t i = UINT64_SIZE;
    double d1 = ((double)(i+1)) / UINT64_SIZE; 
    double d2 = (((double)(i)) / UINT64_SIZE)*16;
    double d3 = ((double)(i * 16)) / UINT64_SIZE;

    std::cout << d1 << " " << d2 << " " << d3; 
}
Run Code Online (Sandbox Code Playgroud)

我期待这样的事情:

0 16 0 
Run Code Online (Sandbox Code Playgroud)

但这是我的输出:

0 16 1 
Run Code Online (Sandbox Code Playgroud)

这里发生了什么?为什么值d3d1不同?

编辑:

我决定将代码更改为此,以查看结果:

#include <stdint.h>
#include <iostream>
#define UINT64_SIZE 18446744073709551615
int main() {
    std::uint64_t i = UINT64_SIZE;
    double d1 = ((double)(i+1.0)) / UINT64_SIZE; //what? 
    double d2 = (((double)(i)) / UINT64_SIZE)*16;
    double d3 = ((double)(i * 16.0)) / UINT64_SIZE;

    std::cout << d1 << " " << d2 << " " << d3; 
}
Run Code Online (Sandbox Code Playgroud)

我现在得到的结果是:

1 16 16 
Run Code Online (Sandbox Code Playgroud)

但是,不应该d1并且d3仍然是相同的值吗?

Iły*_*sov 7

double 通过失去精度而不是从 0 开始溢出(因为它适用于无符号整数)

d1

因此,当您将 1.0 添加到非常大的值 (18446744073709551615) 时,您不会在 0 中得到 0 double,而是类似于 18446744073709551610(注意最后 10 而不是 15)或 1844674440551620,而不是 184467440551620,因此有效数字是 -0 (s) 四舍五入。

现在,您将两个几乎相同的值相除,结果将是 0.9(9)9 或 1.0(0)1,只要double不能保持这么小的值 - 它再次失去精度并舍入为 1.0。

d3

几乎相同,当你将巨大的值乘以 16 - 你得到四舍五入的结果(较少的有效数字被丢弃),通过潜水 - 你得到“几乎” 16,它被四舍五入到 16。