Administrator
Administrator
发布于 2025-11-02 / 14 阅读
0
0

浮点数(IEEE 754)与整数型的运算标准

二进制浮点数算术标准(IEEE口语读:I triple E),许多CPU和浮点运算器运用这种标准;这种标准下,浮点数无法保证小数部分十分精确,这也就是为什么有那个问题:0.1+0.2 != 0.3

IEEE 754转换器(32位):IEEE-754 Konverter für Fließkommazahlen

单精度浮点数(float,32位)

公式:(-1)S × 1.M × 2(E-127)

例如数字292.3,对应二进制表示:0 10000111 00100100010011001010000

  • 首个位表示符号位S(sign),表示数字是正数还是负数,0表示正数,1是负数

  • 中间八位表示指数E(exponent)

  • 最后23位表示尾数M(mantisse)

计算过程:

符号位:-10 = 1

指数:100001112 = 13510 = 135 - 127 = 8,28= 256

尾数:

2-3 + 2-6 + 2-10 + 2-13 + 2-14 + 2-17 + 2-19 = 0.141794204711914

尾数部分隐含了一个1,个位是1,1+0.141794204711914 = 1.141794204711914

最后,1 * 1.141794204711914 * 256 = 292.29931640625

双精度浮点数(double,64位)

原理和32位相似。

公式:(-1)S × 1.M × 2(E-1023)

例如数字292.3456,对应二进制表示

0 10000000111 0010010001011000011110010011110111011001011111110110

  • 首个位表示符号位S(sign),表示数字是正数还是负数,0表示正数,1是负数

  • 中间11位表示指数E(exponent)

  • 最后52位表示尾数M(mantisse),这个部分的结果计算出来要+1,也就是个位数是1

计算过程:

符号位:-10 = 1

指数:100000001112 = 103110 = 1031 - 1023 = 8,28= 256

尾数(太长了,以下**符号是Javascript的幂运算符):

(2**-3+2**-6+2**-10+2**-12+2**-13+2**-18+2**-19+2**-20+2**-21+2**-24+2**-27+2**-28+2**-29+2**-30+2**-32+2**-33+2**-34+2**-36+2**-37+2**-40+2**-42+2**-43+2**-44+2**-45+2**-46+2**-47+2**-48+2**-50+2**-51)+1 = 1.141975

最后,1 x 1.141975 x 256 = 292.3456

有符号整数型的补码

有符号整数(可能有负号)用补码(two's complement)来表示

//数字5在int类型中的二进制:
0b000000000000000000000000 0000 0101
    
//-5的二进制,所有二进制位取反
0b111111111111111111111111 1111 1010
//然后+1
0b111111111111111111111111 1111 1011

//数字100在int类型中的二进制:
0b000000000000000000000000 0110 0100
    
//-100的二进制,所有二进制位取反(补码表示)
0b111111111111111111111111 1001 1011
//然后+1
0b111111111111111111111111 1001 1100

在int(有符号32位整数)类型中:

00000000000000000000000000000000 //所有位都是0表示0

    
0 1111111111111111111111111111111 //表示正数的最大数,也就是2147483647,0和31个1
//由此可见,第一位是类似符号位(sign),表示数值是正数还是负数,0表示正数,1表示负数

    
1 1111111111111111111111111111111 //表示-1
1 1111111111111111111111111111110 //表示-2
1 1111111111111111111111111111101 //表示-3


1 0000000000000000000000000000000 //int的最小值(INT_MIN),-2147483648
//INT_MIN再减1,就会发生截断:
0 1111111111111111111111111111111 //从INT_MIN成为了INT_MAX

无符号整数型

所有比特位(所有字节)都用于表示数值

#include <iostream>
#include <bitset>

int main() {
	char c1[4] = { 'A','B','C','D' };
	unsigned int* i1 = reinterpret_cast<unsigned int*>(c1); //此处c1传入的是char*,是c1数组的首地址,将4个字节的char数组重新解释为uint32
	std::cout << *i1 << std::endl; //1145258561
	std::cout << std::bitset<32>(*i1) << std::endl; //01000100010000110100001001000001
}

由于是小端序(little endian),所以得到的二进制位是反过来的:

| 01000100 | 01000011 | 01000010 | 01000001 |
(D)        (C)        (B)        (A)

为什么值为1145258561?计算过程:

01000100010000110100001001000001

从右向左,将每一位是1的值进行计算,第一个位置是0

1*20 = 1

1*26 = 64

1*29 = 512

1*214 = 16384

.........

1*230 = 1073741824

最后全部相加,得出来就是1145258561

c++技巧

二进制的浮点数赋值给double:

//直接将二进制0b010000...赋值给double效果会和想的不一样
//0b010000...字面量会先转变成long long再把结果的数字赋值给double
//以下会将ll1的逐字节拷贝到double变量中,也可以说拷贝的是比特位

	unsigned long long ll1 = 0b0100000001110010010001011000011110010011110111011001011111110110;
	double d;
	memcpy(&d, &ll1, sizeof(ll1));

//或者:

	unsigned long long ll1 = 0b0100000001110010010001011000011110010011110111011001011111110110;
	double* d = reinterpret_cast<double*>(&ll1);
	std::cout << *d << std::endl; //292.3456

取出double的每个比特位:

#include <iostream>
#include <bitset>
#include <iomanip>

int main() {
	double d1 = 292.3456;
	uint64_t* ll1 = reinterpret_cast<uint64_t*>(&d1);
	std::cout << std::bitset<64>(*ll1) << std::endl; //0100000001110010010001011000011110010011110111011001011111110110
}


评论