“半精度”16位浮点运算

只需要16位存储空间的浮点运算格式正变得越来越流行。也被称为半精密binary16,当内存是稀缺资源时,该格式非常有用。

内容

背景

1985年发布的IEEE 754标准定义了占用32或64位存储空间的浮点数格式。这些格式被称为binary32而且binary64,或更频繁地为而且双精度.多年来MATLAB只使用双精度,它仍然是我们的默认格式。单精度在过去几年里逐渐增加,现在也得到了全面支持。万博1manbetx

2008年发布的IEEE 754修订版定义了一种仅占用16位的浮点格式。被称为binary16,它主要用于降低存储和内存带宽需求。由于它只提供了“一半”精度,它在实际计算中的使用是有问题的。对其作为增加动态范围的图像处理格式的实用程序进行了有趣的讨论工业光与魔法.对于半精度的硬件支万博1manbetx持现在在许多处理器上都是可用的,包括苹果iPhone 7.这里有一个链接到一篇关于半精度的广泛文章NVIDIA GeForce GPU

浮点解剖

浮点数的格式有两个参数,p,分数中的比特数和,指数中的比特数。我将考虑四个精度,季度一半,.四分之一精度格式是我刚刚为这篇博文发明的;它不是标准的,实际上也不是很有用。

四对表征参数为

P = [4,10,23,52];
Q = [3,5,8,11];

有了这些价值观p而且,再加上1位符号,即单词的总比特数,w,是2的幂。

W = p + q + 1
W = 8 16 32 64

归一化数据

大多数浮点数都是归一化,表示为

$$ x = \pm (1+f)2^e $$

分数f在半开区间内

$$ 0 \leq f < 1 $$

$f$的二进制表示最多需要p位。也就是说$2^p $是范围内的整数

$$ 0 \ leq2 ^p f < 2^p $$

指数$e$为范围内的整数

$$ -b+1 \leq e \leq b $$

量$b$既是最大的指数,也是偏见

$$ b = 2^{q-1} -1 $$

B = 2 ^(q-1)-1
B = 3 15 127 1023

规范化数字的小数部分是$1+f$,但只有$f$需要存储。前导$1$被称为隐位

低于正常的

指数$e$有两个值,其中偏置指数$e+b$达到可能表示的最小值和最大值位。最小的是

$$ e + b = 0 $$

相应的浮点数没有隐藏的前导位。这些是低于正常的denormal数字。

$$ x = \pm f 2^{-b} $$

无限和非a数字

最大可能的偏置指数为

$ e + b = 2^q-1 $$。

用这个指数字段表示的量无极而且,或不是一个数字

异常浮点数的百分比,因为它们是次正的,无穷大或NaN随着精度的降低而增加。例外指数只有$2^q$中的$2$值。对于双精度来说,这是$2/2^{11}$,不到千分之一,但对于半精度来说,它是$2/2^5$,超过6%。我的四分之一的浮点数都是特殊的。

对符号位进行编码S = 0对于非负的和S = 1为负。用抵消偏差对指数进行编码,b.然后一个浮点数就可以封装进去了w位与

X = [s e+b 2^p*f]

精度和范围

ε

如果一个实数不能用二进制展开表示,最多需要p比特,它必须近似于一个浮点数,有这样的二进制表示。这是舍入误差.表征精度的重要量是机ε,或每股收益.在MATLAB中,每股收益(x)就是从x到下一个较大的浮点数(绝对值)。毫无疑问,每股收益是简单的区别吗1下一个更大的浮点数。

格式shortgEps = 2.^(-p)
Eps = 0.0625 0.00097656 1.1921e-07 2.2204e-16

这告诉我们,双精度适用于大约16位十进制数字的精度,单精度适用于大约7位十进制数字,一半精度适用于大约3位,四分之一精度适用于略多于1位。

最大浮点数

如果一个实数,或一个算术运算的结果,太大而不能表示,它溢出并被替换.不溢出的最大浮点数是

Realmax = 2.^b.*(2-eps)
Realmax = 15.5 65504 3.4028e+38 1.7977e+308

最小正浮点数

下溢而表示非常小的数字则更加复杂。最小的规范化浮点数是

Realmin = 2.^(-b+1)
Realmin = 0.25 6.1035e-05 1.1755e-38 2.2251e-308

但是有些数字比最小正浮点数.IEEE 754引入了的概念逐渐下溢而且denormal数字。在2008年修订的标准中,他们的名字被改为低于正常的

考虑下底流附近的数字四舍五入。在754之前,浮点数有一个令人不安的特性x而且y可能是不平等的,但他们的差异会这样溢出来x - y就变成了0.与754之间的差距0而且最小正浮点数填充的数字的间距与之间的间距相同最小正浮点数而且2 *最小正浮点数.我喜欢把这个叫做间隔,最小的次法线数,

微小= realmin.*eps
Tiny = 0.015625 5.9605e-08 1.4013e-45 4.9407e-324

浮点整数

flintmax

可以用浮点数来做整数运算。我喜欢叫这些数字燧石.当我们写数字$3$和$3.0$时,它们是对同一个整数的不同描述,但我们认为一个是固定点,另一个是浮点数。最大的火石是flintmax

Flintmax = 2./eps
Flintmax = 32 2048 1.6777e+07 9.0072e+15

严格来说,所有大于的浮点数flintmax是整数,但它们之间的间距大于1,因此将它们用于整数算术是不安全的。只有整数值之间的浮点数0而且flintmax被允许称为燧石。

表格

让我们在一个新的MATLAB中收集所有这些解剖特征表格

T = [w;p;问;b;每股收益;最大浮点数;最小正浮点数;微小的;flintmax];T = table(T(:,1), T(:,2), T(:,3), T(:,4),...“variablenames”, {“季”“一半”“单一”“双”},...“rownames”, {' w '“p”“问”“b”“每股收益”“最大浮点数”“最小正浮点数”...“小”“flintmax”});disp (T)
四分之一半单双________ __________ __________ ___________ w 8 16 32 64 p 4 1023 52 q 35 8 11 b 3 15 127 1023 eps 0.0625 0.00097656 1.1921e-07 2.2204e-16 realmax 15.5 65504 3.4028e+38 1.7977e+308 realmin 0.25 6.1035e-05 1.1755e-38 2.2251e-308 tiny 0.015625 5.9605e-08 1.4013e-45 4.9407e-324 flintmax 32 2048 1.6777e+07 9.0072e+15

Fp8和fp16

3.1版克里夫的实验室包括对象的代码@fp8而且@fp16开始提供四分之一精度和半精度算法的完整实现。

目前提供的方法是

方法(fp16)
类fp16的方法:abs eps isfinite mrdivide rem subsref二进制eq le mtimes round svd ctranspose fix lt ne sign tril diag fp16 lu norm single triu disp ge max + size uminus display gt minus realmax subsasgn double hex mldivide realmin subsindex

这些只提供了部分实现,因为算法不是在紧凑表单上完成的。我们欺骗。对于每个单独的标量操作,将操作数从它们的短存储空间解压缩为老式的双精度操作数。然后,该操作由现有的双精度码执行,结果返回为较短的格式。这模拟了降低的精度和限制的范围,但需要相对较少的新代码。

所有的工作都在构造函数中完成@fp8 / fp8.m而且@fp16 / fp16.m我们可以称之为"解构者"@fp8 / double.m而且@fp16 / double.m.构造函数将普通浮点数转换为精度较低的表示形式,方法是将8位或16位字所能容纳的32位或64位字节打包成尽可能多的数字。解构者则恰恰相反,他们把东西拆开。

一旦这些方法可用,几乎所有其他事情都是微不足道的。大多数操作的代码类似于重载加法的代码。

类型@fp16 / plus.m
函数z = + (x,y) z = fp16(double(x) + double(y));结束

维基百科测试套件

维基百科页面大约一半精度包括几个16位示例,其中符号、指数和分数字段分开。我又加了几个。

0 0 01111 0000000000 = 00101 0000000000 = 2 ^ -10 =每股收益0 01111 0000000000 = 1 +每股收益= 1.0009765625(1)最小的浮动之后下一个1 10000 0000000000 = 2 0 11110 0000000000 = 65504 (max半精密)= 2 ^ 15 * (2-eps) 0 00001 0000000000 = 2 ^ -14 = r ~ 6.10352 e-5(最小正正常)0 00000 1111111111 = r * (1-eps) ~ 6.09756 e-5(最大弱智者)0 00000 0000000001 = r * eps ~ 5.96046 e-8(最小正弱智者)0 00000 0000000000 ~ r * eps / 2(下溢于零)0 00000 0000000000 = 0 000000000000000 = -0 0 11111 0000000000 =无穷大111111 0000000000 = -无穷大0 11111 111111111111 = NaN 0 01101 0101010101 = 0.333251953125 ~ 1/3

这为我的检查提供了测试套件fp16标量的运算。

清零= fp16(0);1 = fp16(1);Eps = Eps(一);R = realmin(1);测试= {' 1 '“每股收益”“1 +每股收益”' 2 '2 / r * (2-eps)”...“r”“r * (1-eps)”“r *每股收益”的r * eps / 2...“零”“零”“1 /零”“1 /零”“零/零”1/3的};

我们来做测试

T = tests(:)' x = eval(T {:});Y = fp16(x);Z =二进制(y);W = double(y);流(' %18s %04s %19.10g %19.10g %s\n'...z,十六进制(y),双(x), w t {:})结束
0 01111 0000000000 3 c00 1 1 1 0 00101 0000000000 1400 0.0009765625 0.0009765625 eps 0 01111 0000000000 3 c01 1.000976563 1.000976563 1 +每股收益1 10000 0000000000 C000 2 2 2 0 11110 0000000000 7 bff 65504 65504 2 / r * (2-eps) 0 00001 0000000000 0400 6.103515625 6.103515625 e-05 e-05 r 0 6.097555161 6.097555161 00000 1111111111 03 ff e-05 e-05 r * (1-eps) 0 00000 0000000001 0001 5.960464478 5.960464478 e-08 e-08 r * eps 0 00000 0000000001 0001 5.960464478 5.960464478 e-08 e-08 r * eps / 2 0 00000 0000000000 0000 0 00000000 0000000000 000000 - 00 11111 0000000000 7C00 Inf Inf 1/ 01 11111 0000000000 FC00 -Inf -Inf -1/ 01 11111 111111111111111111 FFFF NaN NaN 0 / 00 01101 0101010101 3555 0.3333333333 0.3332519531 1/3

矩阵运算

中的大多数方法@fp8而且@fp16处理矩阵。丢勒的《忧郁症II》中的4 × 4魔法方块是我的第一个例子。

清除格式shortgydF4y2BaM = fp16(魔术(4))
M = 16 2 3 13 5 11 10 8 9 7 6 12 4 14 15 1 .单击“确定”

让我们看看打包的16位元素在二进制中是什么样子。

B =二进制(M)
B = 4×4 string array列1 ~ 3 "0 10011 0000000000" "0 10000 0000000000" "0 10000 1000000000" "0 100010 1000000000" "0 10010 0100000000" "0 10010 0010000000" "0 10010 0010000000" "0 10010 0010000000" "0 10010 1000000000" "0 10010 1000000000" "0 10010 1000000000" "0 10010 1000000000" "0 10010 1000000000" "0 01111 0000000000"列4 "0 10010 1010000000" "0 10010 1000000000" "0 10010 1000000000" "0 01111 0000000000"

检查行和是否都相等。这个矩阵向量乘法可以用魔方中的燧石来完成。

e = fp16(ones(4,1)) Me = M*e
e = 1 1 1 1 Me = 34 34 34 34 34

fp16反斜杠

我已经超载mldivide,这样我就可以求解线性系统并计算逆。实际的计算是由lutx,这是我多年前写的一个“教科书”函数,远早于这个半精确的项目。但是现在MATLAB对象系统保证了每一个单独的算术运算都是在解包中完成的fp16数字。

让我们生成一个带有随机两位数整数项的5乘5矩阵。

A = fp16(randi(100,5,5))
A = 76 71 83 44 49 75 4 70 39 45 40 28 32 77 65 66 5 96 80 71 18 10 4 19 76

我要用fp16反斜杠反转一个.所以我需要半精度的单位矩阵。

I = fp16(眼(5))
I = 1 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 1

现在重载反斜杠调用lutx求逆。

X = a \ i
X = -0.0044 0.0346 0.0125 -0.0254 -0.0046 0.0140 -0.0116 0.0026 -0.0046 -0.0002 0.0071 -0.0176 -0.0200 0.0238 0.0008 -0.0060 -0.0020 0.0200 0.0006 -0.0125 0.0003 -0.0052 -0.0072 0.0052 0.0174

计算残差。

Ax = a * x r = I - Ax
Ax = 1.0000 -0.0011 -0.0002 -0.0007 -0.0001 -0.0001 -0.0001 -0.0005 1.0000 -0.0003 -0.0002 -0.0002 -0.0011 -0.0001 0.9995 -0.0002 -0.0000 -0.0001 0.0001 0.0001 -0.0001 1.0000 r = 0 0.0011 0.0002 0.0007 0.0001 0.0001 0.0001 0.0001 0.0001 0.0001 0.010 0.0001 0.0007 0.0001 0.0001 0.0001 0.0005 0 0.0003 0.0002 0.0002 0.0011 0.0001 0.0005 0.0002 0.0000 0.0001 -0.0001 0.0001

这两个斧头而且R都是我所期望的精确到只有三个十进制数字的算术。

尽管我得到了不同的随机结果一个每次我发表这篇博文,我都希望它有一个适度的条件数。

kappa = cond(A)
Kappa = 15.7828

一个如果条件不是很差,我可以求出计算的逆矩阵并期望得到接近原始整数矩阵的结果。

Z = x \ i
Z = 76.1250 71.0000 83.1250 44.1250 49.1250 75.1250 4.0234 70.1875 39.1250 45.1250 40.0625 28.0000 32.0625 77.0000 65.0625 66.1250 5.0234 96.1875 80.1250 71.1250 18.0156 10.0000 4.0156 19.0156 76.0000

fp16圣言

我只是漫不经心地算了一下气孔导度(A).但气孔导度不在重载方法列表中fp16.我惊喜地发现了这一点matlab \ matfun \ cond.m安静地研究这种新的数据类型。下面是代码的核心。

dbtype气孔导度34:43, dbtype气孔导度47
35 s = svd(A);36 if any(s == 0) %处理奇异矩阵37 c = Inf(类(A));38 else 39 c = max(s)./min(s);40如果为空(c) 41 c =零(类(A));42结束43结束47结束

所以用奇异值分解是正确的,我有圣言会过载。SVD计算由一个433行的m文件处理,svdtx,就像lutx,是以前写的fp16存在。

让我们再计算一下SVD。

[U,S,V] = svd(A)
U = -0.5210 -0.4841 0.6802 -0.0315 0.1729 -0.4260 -0.2449 -0.3572 -0.4561 -0.6504 -0.4058 0.4683 0.1633 0.6284 -0.4409 -0.5786 0.0268 -0.5620 0.1532 0.5703 -0.2174 0.6968 0.2593 -0.6104 0.1658 267.5000 S = 0 0 0 0 0 71.1875 55.5000 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 37.3750 16.9531 V = -0.4858 -0.3108 -0.0175 -0.3306 -0.7471 -0.2063 -0.2128 0.9238 0.2195 0.1039 -0.5332 -0.5205 -0.2920 -0.0591 0.5967 -0.4534 0.2891 -0.2050 0.7993 -0.1742 -0.4812 0.7095 0.1384 -0.4478 0.2126

重建一个从它的一半精度SVD。它不太破旧。

Usvt = u * s * v '
Usvt = 75.9375 71.0000 83.0625 44.0313 49.0000 75.0000 4.0117 70.0625 38.9688 45.0000 40.0313 28.0469 32.0313 77.0625 65.0625 66.0000 4.9688 96.0625 80.0000 71.0000 18.0313 10.0234 4.0156 19.0313 76.0000

最后,验证一下我们一直在研究的fp16对象。

名称大小字节类别属性A 5x5 226 fp16 AX 5x5 226 fp16 B 4x4 1576 string I 5x5 226 fp16 M 4x4 208 fp16 Me 4x1 184 fp16 R 5x5 226 fp16 S 5x5 226 fp16 U 5x5 226 fp16 USVT 5x5 226 fp16 V 5x5 226 fp16 5x5 226 fp16 Z 5x5 226 fp16 e 4x1 184 fp16 kappa 1x1 8双

计算器

我介绍了计算器在我的博客中罗马数字.3.1版克里夫的实验室还包括一个更花哨的计算器版本,它以四种不同的精度计算——四分之一、二分之一、单精度和双精度——并以四种不同的格式显示结果——十进制、十六进制、二进制和罗马格式。

我喜欢通过点击键来演示计算器

1 0 0 0 0 / 8 1 =

因为十进制展开是重复的.123456790

谢谢

感谢MathWorkers Ben Tordoff, Steve Eddins和Kiran Kintali,他们提供了半精度工作的背景和指针。




发布与MATLAB®R2017a

|

评论

如欲留言,请点击在这里登录您的MathWorks帐户或创建一个新帐户。