CS61C – 1 : Floating Point

CS61C – 1 : 浮点数

前面比较基础,国内课程基本均有涉及。故笔记从浮点开始

(好吧我就是懒得记了) _(¦3」∠)_

这一部分对应的是第6小节的内容 (参考课程为 [Summer 20] ),重点围绕 IEEE 754 Standard 展开。

为什么引入浮点数?

计算机的数位总是有限的,但自然数是无限的

吾生也有涯,而学海无涯,后面忘了,殆已(

总之固定点数表示数字浪费位数且不灵活,而且单纯的优先位数对小数无能为力

浮点数原理

浮点数基于二进制下的科学计数法,即

$$ \pm 1.xxx\dots_2\times 2^{yyyy\dots} $$

易见上述表达式包含三个部分:

  • 符号位(S):表示正负
  • 指数(Exponent):2的幂次
  • 尾数(Signaificand):小数部分

基于科学计数法, IEEE 754 对单精度浮点数的标准规定如下:

$$ |S(1\ bit)|Exponent(8\ bits)|Significand(23\ bits)| $$

对于指数位,此处标准使用了偏置表示法(Bias = 127),实际指数 = 储存值 – 127,所以计算公式为:

$$ Value = (-1)^S\times(1+Significand)\times 2^{Exponent – 127} $$

IEEE 还预留了特定指数和尾数的组合以表述特殊值

ExponentSignificandMeaning
00$\pm0$
0$\neq$0Denormalized(反规范化数)
1~254anyNormal Number
2550$\pm\infty$
255$\neq$0NAN

其中:

  • NAN 表示无效操作,任何 NAN 参与的运算结果均为 NAN
  • Denormals用于平滑地填充 0 和最小常规数之间的巨大间隙

突然下溢(Abrupt Underflow)间隙(Gap)

前文提及在 IEEE 754 标准中,常规浮点数要求其二进制表示必须被归一化(Normalized),即尾数部分必须满足 1.xxx… 的形式(有一个隐含的前导1)。这使得所有常规数的绝对值都有一个下限,即最小的正常规数。而对于32为单精度浮点数,最小的正常规数为$a=1.0\dots 0_2\times2^{-126}2^{-126}=$,但第二小的正常规数却是$b=1.0\dots 01\times2^{-126}=(1+2^{-23})\times 2^{-126}= 2^{-126}+2^{-149}$

所以我们很容易的得到:

$$|0-a|=2^{-126}\gg|a-b|=2^{-149}$$

即在 0 和 a 之间存在一个巨大的“鸿沟”,也就是间隙;这个区间内的数字会直接被刷新为 0 ,也就是下溢。

Denormals的具体表示方法为:

  • 指数域(Exponent Field) 全为 0
  • 尾数域(Significand Field) 不为 0
  • 不包含隐含的前导1。尾数就是 $0.xxx\dots$ 的形式

此时的计算公式为:

$$Value = (-1)^S\times (0.Significand)\times 2^{-126}$$

现在,最小的正数不再是 $2^{-126}$,而是: $min_{denormal} = 0.0…01_2 \times 2^{-126} = 2^{-23} * 2^{-126} = 2^{-149}$

这使得数值可以逐渐趋近于0而不是突然断开。因此,这个过程也称为 “渐进下溢(Gradual Underflow)”。

精度与准确性

精度(Precision):位数多少,表示细节能力

准确性(Accuracy):与真实值的接近程度

高精度$\neq$ 高准确性,例如:float pi = 3.14; 精度高但不准确

舍入模式

IEEE 754 定义了四种舍入模式:

Round toward $+\infty$ – 正无穷舍入

Round toward $-\infty$ – 负无穷舍入

Truncate – 向零舍入 以及 Round to nearest,ties to even – 向最近偶数舍入

浮点数运算

浮点数的运算是非结合的,因为大数会忽略小数,导致误差积累

类型转换

在大部分语言中强制类型转化可能会造成精度损失带来难以预料的bug

i == (int)((float)i)    // Not always true
f == (float)((int)f)     // Not always true either

其他格式

除单精度浮点数外,还有其他浮点形式,比如

  • 双精度(double, binary64):1位符号 + 11位指数 + 52位尾数
  • 半精度(fp16, binary16):1位符号 + 5位指数 + 10位尾数
  • BFLOAT16:1位符号 + 8位指数 + 7位尾数(机器学习中很常见)

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注