我一直想知道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;
}
我期待这样的事情:
0 16 0
但这是我的输出:
0 16 1
这里发生了什么?为什么d3
和d1
的值不同?
编辑:
我决定将我的代码更改为此以查看结果:
#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;
}
我现在得到的结果是:
1 16 16
但是,d1
和d3
不应该是相同的值吗?
答案 0 :(得分:4)
double
通过失去精度而溢出,而不是从0开始(因为它与无符号整数一起使用)
<强> D1 强>
所以,当你将1.0加到非常大的值(18446744073709551615)时,你在double
中没有得到0,但是像18446744073709551610(注意最后10而不是15)或18446744073709551620(注意最后20)而不是15),所以 - 不太重要的数字是四舍五入的。
现在,您将两个几乎相同的值分开,结果将是0.9(9)9或1.0(0)1,只要double
无法保持这么小的值 - 再次失去精度和舍入到1.0。
<强> D3 强>
几乎相同的是,当你有多个巨大值16时 - 你得到圆润的结果(不太重要的数字被抛弃),通过潜水 - 你得到的几乎&#34;几乎&#34; 16,舍入到16。
答案 1 :(得分:2)
这是精度损失的情况。请考虑以下事项。
#include <stdint.h>
#include <iostream>
#define UINT64_SIZE 18446744073709551615
int main() {
std::uint64_t i = UINT64_SIZE;
auto a = i;
auto b = i * 16;
auto c = (double)b;
auto d = (uint64_t)c;
std::cout << a << std::endl;
std::cout << b << std::endl;
std::cout << c << std::endl;
std::cout << d << std::endl;
return 0;
}
在我的系统上输出如下。
18446744073709551615
18446744073709551600
1.8446744073709552e+19
9223372036854775808
在这种情况下, double
根本没有足够的精确度。
编辑:还有一个舍入问题。当您使用UINT64_SIZE
预先形成除法时,会将分母提升为双倍,并且您将得到介于0.0和1.0之间的小数值。小数不会四舍五入。实际值非常接近1.0,并在推到std::cout
时向上舍入。
在你的问题中,你会问“如果双倍达到它的最大值,会发生什么”。请注意,在您提供的示例中,double
没有接近它的最大值。只有它的精度超过了。当超过double
的精度时,将丢弃多余的精度。