Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does a calculation error occur when this double value is multiplied by 2 in C++?

#include <iostream>
#include <stdint.h>
#include <bitset>
#include <sstream>
#include <string>
#include <iomanip>
using namespace std;
int main()
{
    uint64_t int_value = 0b0000000000001101000011110100001011000000110111111010111101110011;
    double double_value = (*((double *)((void *)&int_value)));
    printf("double initiate value: %e\n", double_value);
    cout << "sign " << setw(11) << "exp"
         << " " << setw(52) << "frac" << endl;
    for (int i = 0; i < 10; i++)
    {
        stringstream ss;
        ss << bitset<64>((*((uint64_t *)((void *)&double_value))));
        auto str = ss.str();
        cout << setw(4) << str.substr(0, 1) << " " << setw(11) << str.substr(1, 11) << " " << str.substr(12, 52) << endl;
        double_value *= 2;
    }
}

Binary output with ieee754 standard format:

double initiate value: 1.816163e-308
sign         exp                                                 frac
   0 00000000000 1101000011110100001011000000110111111010111101110011
   0 00000000001 1010000111101000010110000001101111110101111011100110
   0 00000000010 1010000111101000010110000001101111110101111011100110
   0 00000000011 1010000111101000010110000001101111110101111011100110
   0 00000000100 1010000111101000010110000001101111110101111011100110
   0 00000000101 1010000111101000010110000001101111110101111011100110
   0 00000000110 1010000111101000010110000001101111110101111011100110
   0 00000000111 1010000111101000010110000001101111110101111011100110
   0 00000001000 1010000111101000010110000001101111110101111011100110
   0 00000001001 1010000111101000010110000001101111110101111011100110

The double value is multiplied by 2, 10 times.

I think only the exp part should change (increasing by 1), but on my system, the frac part also changes.

like image 761
W.Z.Hai Avatar asked Mar 02 '23 16:03

W.Z.Hai


1 Answers

You are running into denormalised numbers. When the exponent is zero, but the mantissa is not, then the mantissa is used as is without an implicit leading 1 digit. This is done so that the representation can handle very small numbers that are smaller than what the smallest exponent could represent. So in the first two rows of your example:

              v
0 00000000000 1101000011110100001011000000110111111010111101110011
0 00000000001 1010000111101000010110000001101111110101111011100110

the leading 1 which I have indicated with v is removed when the exponent is incremented to a nonzero value.

You should also note that the exponent value is biased which means 0000000001 is not an exponent of 1, it is an exponent of -1022.

like image 101
Greg Hewgill Avatar answered Mar 04 '23 05:03

Greg Hewgill