float16和int16的表达解析
为什么用同样多的比特(16位),float16 既能表示小数,其数值范围还比 int16 大得多?下面给你解读这奇怪表面的背后真相。
int16像一把刻度均匀的尺子,只能精确地量整数。float16像一把刻度不均匀的放大镜,可以在不同区域调节精度,从而覆盖更广的范围。
int16
- 编码规则:使用 “二进制补码”。第 1 位(最高位)是 符号位:
0代表正数,1代表负数。剩下的 15 位是 数值位。 - 范围计算:
- 正数最大:
0 111 1111 1111 1111= +2¹⁵ - 1 = +32,767 - 负数最小:
1 000 0000 0000 0000= -2¹⁵ = -32,768 - 每个整数都有一个唯一的编码,从 -32768 到 32767,一个接一个,没有间隔。
- 正数最大:
float16 的设计借鉴了科学计数法的思想。在科学计数法中,一个数可以表示为:±M × 10^E(例如:6.02 × 10²³)。在 float16 的二进制世界里,它被表示为:±Sign × (1 + Fraction) × 2^(Exponent - Bias)(float16 的指数偏移量是 15)。它的 16 位被分成了三个部分:
- 符号位 (Sign):1 位。和
int16一样。 - 指数位 (Exponent):5 位。这决定了数的尺度或范围。可以表示 0 到 31。
- 尾数位/小数位 (Fraction/Mantissa):10 位。这决定了在特定尺度下的精度。
float16 的范围:约 -6.55×10⁴ 到 +6.55×10⁴,并且还能向下(0)延伸到约 ±5.96×10⁻⁸
秘诀在于 指数。5 位的指数位,通过一个“偏移码”处理,可以让指数大约在 -14 到 +15 之间变化(除去指数全0表示非规约数或零、全1表示无穷大或NaN这2个特殊情况)。
这意味着它可以表示 2^(-14) 这样极小的数,和 2^(15) 这样极大的数。2^15 = 32768,再乘上尾数部分(约 1.999),就能得到大约 65,504。
**float16**就像一个可移动的聚光灯在一个非常长的数字轴上巡逻。
- 指数 控制聚光灯移动到哪里。它可以照到非常远(大数)或非常近(小数)的地方。
- 尾数 控制在聚光灯光圈内的精细刻度。光圈的大小是固定的(由 10 位决定),所以:
- 当聚光灯照在“小数区域”时,光圈覆盖的范围很小,刻度非常密集,精度很高。
- 当聚光灯照在“大数区域”时,光圈覆盖的范围很大,刻度变得稀疏,精度下降。比如,在 50000 附近,
float16甚至无法区分 50000 和 50001,它们会被表示为同一个数!
总结与对比
| 特性 | int16 | float16 |
|---|---|---|
| 设计思想 | 直接计数 | 科学计数法 |
| 核心部件 | 符号位 + 数值位 | 符号位 + 指数位 + 尾数位 |
| 数值范围 | 较小 (-32,768 到 32,767) | 极广 (约 ±65,504,下至 ±0.00000006) |
| 小数支持 | 否 | 是 |
| 精度 | 绝对精确(在范围内) | 近似表示,精度随数值大小变化 |
| 主要用途 | 计数、索引、货币(需要精确) | 机器学习、图形处理、科学计算(容忍误差) |
结论:
float16 之所以能用同样的 16 位做到“鱼与熊掌兼得”(范围广 + 有小数),是因为它牺牲了均匀的精度,采用了基于指数的“科学计数法”编码。这是一种用“精度”来换取“范围和灵活性”的权衡。而 int16 则用它的所有位来保证在有限范围内的绝对精确。
最后说说整数的补码:
把16位二进制想象成一个16位的时钟,总共65536个刻度(0~65535)。
技巧:要表示 -X,就顺时针走 (65536 - X) 格
例子:-1 怎么表示?
- 顺时针走 (65536 - 1) = 65535 格
- 65535 的二进制 =
1111111111111111
验证为什么这代表 -1:
111...111 + 000...001 = 000...000 (溢出,回到0点)
就像时钟上:11点59分 + 1分钟 = 12点整
再想想:-2怎么表示?
所以补码的本质是:用“大数”来表示“负数”,让加减法统一用加法电路就能完成!
- 点赞
- 收藏
- 关注作者
评论(0)