pg float4与oracle的binary_float 比较

oracle 的binary_float, binary_double (二进制精度)与 pg 的 浮点数(二进制精度)都遵循IEEE Standard 754-1985 (IEEE754) ,

oracle 中 float 实际为 NUMBER类型,是十进制浮点数。

一 IEEE754

以单精度浮点数为例,IEEE754 规定 单精度浮点数的内存格式如下为:1bit 符号位 + 8bit 阶码 + 23bit 尾数。

  • 阶码为指数的实际值加上某个固定的值,固定值为 2^(e -1) - 1, e 为存储指数的比特的长度, 单精度浮点数为8,也即固定值为 127.
  • 尾数部分采用原码表示,且尾数码隐含了最高位为 1,在计算时我们需要加上最高位1,即 1.M。

由于这种阶码 + 尾数的表示方式,使得浮点数的值形式有多种。

  • 零:指数0,小数0;

  • 非规范化:指数0,小数 大于0小于1;此时尾数码隐含了最高位为 0

  • 规范化:指数 1~2^8 -1,小数[1,2);

  • 无穷:指数 2^8 -1 ,小数0;

  • NaN:指数 2^8 -1,小数非0。

在将10进制的浮点数转换为二进制时, 尾数部分可能远远超过23位,此时就需要进行舍入。IEEE754 支持如下4种。

  1. round to nearest. 舍入到最接近(默认),在一样接近的情况下偶数优先(当存在两个数一样接近的时候,则取其中的偶数(在二进制中则以0结尾, ))

  2. 朝正无穷舍入 ceil(1.324) = 2

  3. 朝负无穷舍入 floor(1.234)= 1

  4. 朝0舍入,就是截断(int) 1.324= 1

pg 和 oracle 都是采用默认的舍入到最近。下面对二进制舍入进行说明:

​ 如果有效数字超出规定数位的多余数字是1001,它大于超出规定最低位的一半(即0.5 ,1000),故最低位进1。如果多余数字是0111,它小于最低位的一半,则舍掉多余数字(截断尾数)即可。对于多余数字是1000、正好是最低位一半的特殊情况,最低位为0则舍掉多余位,最低位为1则进位1、使得最低位仍为0(偶数)。

​ 如浮点数123456781012345678901234567890123456789,的二进制为1.011100111000001110100101000000011001101011000100011000000000000000000000000000000000000000000000000000000000000000000000000000 ,超出 23 位部分为1000000011001101011000100011000000000000000000000000000000000000000000000000000000000000000000000000000 由于大于最低位的一半, 进位 1, 结果为 1.01110011100000111010011

十进制小数数转为二进制

通过对小数部分乘2,然后取整数, 然后再取小数部分乘2,然后取整数, 循环直到没有小数,或到达所需精度。

如下:

0.625=(0.101)B
0.625*2=1.25======取出整数部分1
0.25*2=0.5========取出整数部分0
0.5*2=1==========取出整数部分1 

二进制小数转换为10进制

根据如下公式 bit(1)*2^-1 + bit(2)*2^-2 + bit(3)*2^-3 + bit(4)*2^-4 + ... + bit(i)*2^-i 即可, bit(i) 表示小数点后第i位的值。

如:

0.101
1*0.5 +0*0.25 +1+0.125 = 0.625

二 比较

由于都遵循IEEE754, pg 与 oracle 中实际保存的浮点数相同。通过如下案例进行说明:

create table test(col1 binary_float);
-- pg 中create table test(col1 float4);
insert into test values (123456789012345678901234567890123456789);
--  oracle中通过dump 函数查看内存
select dump(col1, 16) from test;

--  pg中通过 float4send 函数查看内存
select float4send(col1) from test;

oracle dump:
fe,b9,c1,d3

pg float4send:
7e,b9,c1,d3

在 oracle 中:

  • 阶码为253 指数位126
  • 尾数 1.01110011100000111010011
  • 符号位 为1 ? 这部分不清楚为什么为1, 可能是由于dump 函数问题。

在 pg 中:

  • 阶码为253 指数位126

  • 尾数 1.01110011100000111010011

  • 符号位为 0, 表示正数

上述内存表示的浮点数为 1.2345678605116651e+38

但在oracle 中显示为 1.23456786E38 , pg 中显示为 1.2345679e+38, 位数不同。

pg 中对于 extra_float_digits 大于 0 (在pg13 中默认为1) 会调用float_to_shortest_decimal_buf 去获取 shortest decimal representation 。

三 参考资料

  1. Float-point number

  2. IEEE754 浮点数:简读+案例=秒懂

  3. 消灭烦人的IEEE754困惑–知识梳理

更多推荐