Double overflow?

人盡茶涼 提交于 2021-02-04 17:36:08

问题


I have always wondered what happens in case a double reaches it's max value, so I decided to write this code:

#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; 
}

I was expecting something like this:

0 16 0 

But this is my output:

0 16 1 

What is going on here? Why are the values of d3 and d1 different?

EDIT:

I decided to change my code to this to see the result:

#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; 
}

The result I get now is this:

1 16 16 

However, shouldn't d1 and d3 still be the same value?


回答1:


double overflows by loosing precision, not by starting from 0 (as it works with unsigned integers)

d1

So, when you add 1.0 to very big value (18446744073709551615), you're not getting 0 in double, but something like 18446744073709551610 (note last 10 instead of 15) or 18446744073709551620 (note last 20 instead of 15), so - less significant digit(s) are rounded.

Now, you're dividing two almost identical values, result will be either 0.9(9)9 or 1.0(0)1, as soon as double cannot hold such small value - again it looses precision and rounds to 1.0.

d3

Almost the same, when you multiple huge value by 16 - you're getting rounded result (less significant digits are thrown away), by diving it - you're getting "almost" 16, which is rounded to 16.




回答2:


This is a case of loss of precision. Consider the following.

#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;
}

On my system the output is as follow.

18446744073709551615
18446744073709551600
1.8446744073709552e+19
9223372036854775808

double simply doesn't have enough precision in this case.

Edit: There is also a rounding problem. When you preform the division with UINT64_SIZE the denumerator is promoted to double and you are left with a decimal value between 0.0 and 1.0. The decimals are not rounded off. The actual value is very near 1.0 and is rounded up when pushed to std::cout.

In your question you ask "what happens in case a double reaches it's max value". Note that in the example you provided no double is ever near it's maximum value. Only it's precision is exceeded. When a double's precision is exceeded, the excess precision is discarded.



来源:https://stackoverflow.com/questions/41492080/double-overflow

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!