前言
刚开始看计算机图形学,开篇就看见建议使用双精度double进行几何计算,使用单精度float进行色彩计算,对于占用大量内存的数据,例如三角形网格,建议存储float数据,但在通过成员函数访问数据时,将其转换为double。
为啥呢?
现代体系结构表明,降低内存使用和保持一致的内存访问是提高效率的关键。
想起之前看计算机组成原理的时候直接略过了浮点数这一章,赶紧去补了一下基础
一、十进制数如何表示?
十进制数表示法使用的表达形式是这样的:
d
m
d
m
−
1
.
.
.
d
1
d
0
.
d
−
1
d
−
2
.
.
.
d
−
n
d_md_{m-1}...d_1d_0.d_{-1}d_{-2}...d_{-n}
dmdm−1...d1d0.d−1d−2...d−n
其中每个十进制数
d
i
d_i
di取值范围在0~9之间,因此
d
=
∑
i
=
−
n
m
1
0
i
∗
d
i
d=\sum_{i=-n}^m10^i*d_i
d=∑i=−nm10i∗di
其中i为权,小数点左边从0开始变大,小数点右边从-1开始变小
示例: 12.3 4 10 = 1 ∗ 1 0 1 + 2 ∗ 1 0 0 + 3 ∗ 1 0 − 1 + 4 ∗ 1 0 − 2 12.34_{10}=1*10^1+2*10^0+3*10^{-1}+4*10^{-2} 12.3410=1∗101+2∗100+3∗10−1+4∗10−2
二、二进制数如何表示?
1.定点表示法
和十进制数差不多,二进制数也能表示为相似的权值模式: b = ∑ i = − n m 2 i ∗ b i b=\sum_{i=-n}^m2^i*b_i b=∑i=−nm2i∗bi
示例: 101.1 1 2 = 1 ∗ 2 2 + 0 ∗ 2 1 + 1 ∗ 2 0 + 1 ∗ 2 − 1 + 1 ∗ 2 − 2 101.11_{2}=1*2^2+0*2^1+1*2^0+1*2^{-1}+1*2^{-2} 101.112=1∗22+0∗21+1∗20+1∗2−1+1∗2−2
所以二进制数小数点左移一位,相当于乘以2,右移一位相当于除以2
小技巧:形如
0.11...
1
2
0.11...1_2
0.11...12的二进数表示的是刚好比1小的数,例如
0.11111
1
2
0.111111_2
0.1111112表示的是
63
/
64
63/64
63/64
2.浮点数表示法
IEEE浮点标准:
V
=
(
−
1
)
s
∗
M
∗
2
E
V=(-1)^s*M*2^E
V=(−1)s∗M∗2E
通过上面的式子表示一个数
其中:
- 符号(sign):s决定这个数的正负,1为负数,0为正数
- 尾数(significand):M为二进制小数,可以看做长这样 1.1101..11 1.1101..11 1.1101..11之类的,M = 1 + frac
- 阶码(exponent):E的作用是对浮点数加权,权重是2的E次幂
对于32位的浮点数,最高1位是符号位S,接着是8位的指数E,剩下的23位是有效数字M。
对于64位的浮点数,最高1位是符号位S,接着的11位是指数E,剩下的52位是有效数字M。
计算机存储是按位进行存储,因此需要弄清楚每一位代表是什么意思,下面是二进制位的阐述
- 符号位s
- k位的阶码字段 e x p = e k − 1 . . . e 1 e 0 exp=e_{k-1}...e_1e_0 exp=ek−1...e1e0,注意要先让阶码+127,然后转换为2进制的k位阶码,用于偏置
- n位的小数字段 f r a c = f n − 1 . . . f 1 f 0 frac=f_{n-1}...f_{1}f_0 frac=fn−1...f1f0,小数字段 0 < = f < 1 0<=f<1 0<=f<1,隐含了以1开头的表示,因为之前把M看做了 1. f n − 1 . . . f 1 f 0 1.f_{n-1}...f_1f_0 1.fn−1...f1f0这样的数字,所以第一位总是1,可以获得一个额外的精度
这里有规格化、非规格化、无穷大和NaN四种情况
- 规格化:
正常表示 - 非规格化:
表示数值0,因为规格化表示中总是让M>=1 - 无穷大:
s=0,正无穷;s=1,负无穷 - NaN:
特殊值,表示不是一个数(Not a Number)
示例:
2.1 注意事项
- 对于有效数字M,因为其值大于等于1而小于2,所以在计算机中,为了能够利用23位(52位)表示更多的数据,IEEE754规定保存M时默认这个数的第一位为1,所以只保存后面的部分(小数点后的位)。然后等到读取此数的时候,再把第一位的1加上去。
- 对于指数E,为了能够表示负数的指数,IEEE754规定,存入内存时E的真实值必须再加上一个中间数,对于8位的E,这个中间数为127;对于11位的E,这个中间数是1023。
- 当E全为0时,读取该数字时有效数字M不再加上第一位的1,因为这是一个无限接近与0的数字,表示正负0;当E全为1的时候,若M全为0,则表示一个正负无穷大的数。
三、整型与浮点型数据在计算机内存中的存储
看到别的博文有总结,也写进来好了
1.整形归类
整型家族:char、unsigned char、signed char //对于char来说,标准里并不默认其为有符号还是无符号的,这个结果取决于编译器。在计算机中,我们实际上可以用整形数据来模拟定点数运算。
short ( signed short [int] )、unsigned short [int]
int ( signed int )、unsigned int
long ( signed long [int] )、unsigned long [int]
浮点数家族:
float
double
构造类型:数组类型
结构体类型 struct
枚举类型 enum
联合类型 union
指针类型
空类型
2.整型在内存中的存储
- 整型在内存中以补码的形式存储,浮点数则没有补码之说,它只需要规定指数与尾数。
原因有三:其一,使用补码可以将符号位和数值域统一处理;其二,加法和减法可以统一处理(cpu只有加法器);其三,补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路。 - 数据存储的大小端模式 小端字节序存储:数据的低字节存到低地址处,高字节存到高地址处。
大端字节序存储:数据的低字节存到高地址处,高字节存到低地址处。
存在不同字节序存储模式的原因:因为在计算机系统中是以字节做单位的,每个地址单元对应一个字节,一个字节有8个比特位。不过在c语言中,除了具有一个字节(8b)的char类型外,还有2个字节(16b)的short型、4个字节(32b)的int型等,而且对于位数大于8的处理器,由于寄存器宽度大于一个字节,所以就必然存在如何安排多个字节的问题。这就是小端存储模式与大端存储模式出现的原因。
例如对于一个short类型的x数据,在内存中的地址为0x0010,其值为0x1122,那么0x11是x的高字节,0x22是x的低字节。对于大端存储模式,0x11放在低地址的0x0010中,0x22放在高地址的0x0011中。对于小端存储模式则相反。