为什么在C++中这个double值乘以2会出现计算错误?
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;
}
}
采用 ieee754 标准格式的二进制输出:
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
double值乘以2,10倍
我认为只有 exp 部分应该改变(增加 1),但在我的系统上,frac 部分也会改变。
您 运行 进入 denormalised numbers。当指数为零但尾数不是零时,尾数按原样使用 而没有 隐含的前导 1 数字。这样做是为了让表示可以处理非常小的数字,这些数字小于最小指数可以表示的数字。所以在你的例子的前两行:
v
0 00000000000 1101000011110100001011000000110111111010111101110011
0 00000000001 1010000111101000010110000001101111110101111011100110
当指数递增到非零值时,我用 v
指示的前导 1 将被删除。
您还应该注意指数值是 biased 这意味着 0000000001
不是 1 的指数,它是 -1022 的指数。
#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;
}
}
采用 ieee754 标准格式的二进制输出:
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
double值乘以2,10倍
我认为只有 exp 部分应该改变(增加 1),但在我的系统上,frac 部分也会改变。
您 运行 进入 denormalised numbers。当指数为零但尾数不是零时,尾数按原样使用 而没有 隐含的前导 1 数字。这样做是为了让表示可以处理非常小的数字,这些数字小于最小指数可以表示的数字。所以在你的例子的前两行:
v
0 00000000000 1101000011110100001011000000110111111010111101110011
0 00000000001 1010000111101000010110000001101111110101111011100110
当指数递增到非零值时,我用 v
指示的前导 1 将被删除。
您还应该注意指数值是 biased 这意味着 0000000001
不是 1 的指数,它是 -1022 的指数。