分类
tech

关于浮点数(1)EncodeFloatRGBA & DecodeFloatRGBA

分析一下 EncodeFloatRGBA() & DecodeFloatRGBA() 的实现

翻翻 Unity Built in Shaders 是一个涨姿势的活动。。。

其中,EncodeFloatRGBA 函数将1个32bit的float数据(取值范围0~1之间)转换为float4类型。因此 EncodeFloatRGBA 相当于把一个32bit的float拆成了4份。 DecodeFloatRGBA 函数则将float4还原为float。

十进制浮点数与二进制浮点数

十进制

每位代表10的幂,因此有:

其中,每一位的范围为 0~9, 例如

二进制

每位代表2的幂,因此有:

其中,每一位的范围为 0~1, 例如

Float32 的表示规格

符号位s: 代表此Float的正负,1为负,0为正。

指数位e: 代表此Float的指数范围,8bit可表示的数值范围为0~255,由于指数有正负符号,因此国际标准IEEE 754规定,指数需要减去127,由此将0~255映射到了-127~128的范围内。

底数位m: 代表此Float的底数,也就是Float内真正存储的数值,由于科学记数法的底数形式,得出Float小数点前的第一位总是为1(在二进制中),因此省去最高位的1不存储,例如1.00101、1.11010等等会被保存为00101、11010,因此23-bit的底数实际可以表示24-bit位的数据。

1234.5678 as an example

将十进制数 1234.5678 用 float32 来表示:

将十进制浮点数1234.5678 转换为二进制浮点数为10011010010.1001000101011

用科学计数法表示,挪动小数点得到最高位的1的右侧,并记录挪动位数

10011010010.1001000101011 = 1.00110100101001000101011 * 210

由此,可以计算出符号位s,指数位e,底数m

符号位s = 0,(正数s=0)

指数位e = 10001001,(挪动了小数点10次,10 + 127 = 137,再转换为二进制)

底数位m = 00110100101001000101011,(省略最前面的1)

最终计算出的32-bit Float 为

0100 0100 1001 1010 0101 0010 0010 1011

EncodeFloatRGBA 分析

EncodeFloatRGBA函数输入参数为float v, 返回参数为float4 enc,其中的内部数据变化可以图解如下:

下面以 float v = 0.123456789 为例,追踪这个函数内部的数据变化

首先将v = 0.123456789转换为二进制,则v = 0.000111111001101011011101010

根据代码

这个点乘的意义,从二进制的角度来看是移位,256 = 28 , 65536 = 216 , 16777216 = 224

从十进制的角度来理解,12.34 * 101 = 123.4,就是将小数点右移一位,指数代表移动位数

对二进制同样,1101.1011 * 22 = 110110.11,小数点右移两位,指数为负则左移小数点

因此,对v* float4(20, 28, 216, 224),意味着将v的小数点分别右移0、8、16、24位

结果为:

下一步代码

enc = frac (enc);

frac() 函数代表舍去整数部分,只保留分数

下一步代码

与前面同理,1/256 = 2-8, 意味着将二进制数的小数点左移8位

再用 enc - tmp 使得各分量后相同位数被消除(保留前8位)

最后一位的 enc.a 因为也减去了 tmp.w 因此会产生一些误差,tmp.w非常小可以忽略不计

最后,对比v和enc的值

可以看出,得到了与最初图示相同的结果。注意enc.a 虽然有误差,但 0.00111111110 与 0.010 差值很小,可以忽略不计。

DecodeFloatRGBA 分析

DecodeFloatRGBA函数与EncodeFloatRGBA作用相反,它的输入参数为float4 enc, 返回参数为float v,其中的内部数据变化依旧可以图解如下:

根据代码,

kDecodeDot = float4(1.0, 1/256.0, 1/65536.0, 1/16777216.0)
  = float4(20,  2-8 , 2-16 , 2-24)

因此将它与 enc 点乘的结果,就是将enc的rgba分量的小数点分别左移相应的位数再求和

举例:

这样就还原了 float v 的数值