2024年2月19日发(作者:)

number数据内部存储时,以变长的数组来存放,数组里的每个元素占一个字节,最多20个元素。内部代码为2。number数据的存放格式为:<[length]>,sign bit/exponent,digit1,digit2,…,digit20

sign bit/exponent这部分叫做exponent byte。

exponent byte包括三部分:

sign bit:这表示高位bit,也就是128。并且我们有:

1. 如果小于128,则数值为负数。

2. 如果大于等于128,则数值为正数或0。

offset,始终为65

exponent:其范围从-65到62。该部分的值是基于100而进行的科学计数法。

为0时比较特殊,就只有sign bit而没有offset和exponent,也就是128。比如:

SQL> select dump(0) from dual;

DUMP(0)

----------------

Typ=2 Len=1: 128

来看一个非0的值:

SQL> select dump(25,16) from dual;

DUMP(25,16)

------------------

Typ=2 Len=2: c1,1a

则exponent byte为c1,也就是

SQL> select to_number('c1','xx'),to_number('1a','xx') from dual;

TO_NUMBER('C1','XX') TO_NUMBER('1A','XX')

-------------------- --------------------

193 26

而193=128+65+0,也就是sign bit为128,offset为65,exponent为0。

同时,oracle存储时,用1表示0,2表示1,依此类推。也就是说用显示的值减1就是实际的值。如下所示:

SQL> select dump(1,16) from dual;

DUMP(1,16)

-----------------

Typ=2 Len=2: c1,2

因此,0xc11a就是:(26-1)*power(100,0)=25

再来看另一个例子:

SQL> select dump(1234,16) from dual;

DUMP(1234,16)

--------------------

Typ=2 Len=3: c2,d,23

这里c2表示194,也就是194=128+65+1。

SQL> select to_number('c2','xx'),to_number('d','xx'),to_number('23','xx') from dual;

TO_NUMBER('C2','XX') TO_NUMBER('D','XX') TO_NUMBER('23','XX')

-------------------- ------------------- --------------------

194 13 35

因此该数值为:(13-1)*power(100,1)+(35-1)*power(100,0)=1200+34=1234

如果数值为负数,则它的exponent byte的算法是一样的,只不过顺序是反过来的,从255开始计算。比如:

SQL> select dump(-25,16) from dual;

DUMP(-25,16)

---------------------

Typ=2 Len=3: 3e,4c,66

SQL> select to_number('3e','xx'),to_number('4c','xx') from dual;

TO_NUMBER('3E','XX') TO_NUMBER('4C','XX')

-------------------- --------------------

62 76

负数的最后一位始终都是66,也就是说最后一位如果为66,则说明它是负数。

这时的exponent byte为:255-62=193=128+65+0

同时,oracle在存放number型数据时,以100为基数,同时正数和负数互为相反数,也就是正数+负数=100。因此我们就有:(100-76-1)*power(100,0)=25

加上符号位,则数值为-25

我们在来看一个例子:

SQL> select dump(-1234,16) from dual;

DUMP(-1234,16)

------------------------

Typ=2 Len=4: 3d,59,43,66

SQL> select to_number('3d','xx'),to_number('59','xx'),to_number('43','xx') from dual;

TO_NUMBER('3D','XX') TO_NUMBER('59','XX') TO_NUMBER('43','XX')

-------------------- -------------------- --------------------

61 89 67

于是exponent byte为:255-61=194=128+65+1

(100-89-1)*power(100,1)+(100-67-1)*power(100,0)=1200+24=1234

加上符号位,则数值为-1234

因此很明显,如果第一个字节大于等于128,则说明该数值为正数,并且exponent为:exponent = first byte

- 128 - 65 = first byte - 193

如果第一个字节小于128,则说明该数值为负数,并且exponent为:

exponent = (255 - first byte) - 128 - 65 = 62 - first byte

对于小数来说:

SQL> select dump(1234567.89,16) from dual;

DUMP(1234567.89,16)

-----------------------------

Typ=2 Len=6: c4,2,18,2e,44,5a

exponent为:0xc4,也就是196,也就是196-193=3

digits分别为:

0x2=2-1=1=1*power(100,3)=1000000

0x18=24-1=23*power(100,2)=230000

0x2e=46-1=45*power(100,1)=4500

0x44=68-1=67*power(100,0)=67

0x5a=90-1=89*power(100,-1)=0.89

把它们都加起来,也就是1234567.89

再来看一个例子:

SQL> select dump(123456789.9876,16) from dual;

DUMP(123456789.9876,16)

-----------------------------------

Typ=2 Len=8: c5,2,18,2e,44,5a,63,4d

exponent为:0xc5,也就是197,也就是197-193=4

digits分别为:

0x2=2-1=1=1*power(100,4)=100000000

0x18=24-1=23*power(100,3)=23000000

0x2e=46-1=45*power(100,2)=450000

0x44=68-1=67*power(100,1)=6700

0x5a=90-1=89*power(100,0)=89

0x63=99-1=98*power(100,-1)=.098

0x4d=77-1=76*power(100,-2)=0.0076

把它们都加起来,就是123456789.9876

而对于负数来说,比如:

SQL> select dump(-123456.789,16) from dual;

DUMP(-123456.789,16)

--------------------------------

Typ=2 Len=7: 3c,59,43,2d,17,b,66

Exponent => 0x3c= 62(dec) - 60 = 2

Digits分别为:

0x59 = 89(dec): 101 - 89 = 12 > 12 * 100^2 = 120000

0x43 = 67(dec): 101 - 67 = 34 > 34 * 100^1 = 3400

0x2d = 45(dec): 101 - 45 = 56 > 56 * 100^0 = 56

0x17 = 23(dec): 101 - 23 = 78 > 78 * 100^-1 = .78

0xb = 11(dec): 101 - 11 = 90 > 90 * 100^-2 = .009

sum = 123456.789 (-)

忽略最后一位的符号位:0x66 = 102(dec)

oracle数值使用100作为基准,因此,每个digit都表示一个0到99的数值。总共最多使用20个digit来表示一个数值。

在比较数值大小时,oracle从左边开始比较每个digit,直到最后一个digit。比如,对于4和3来说:4表示为<193,5>。3表示为<193,4>,5大于4,因此,4>3。

而对于-4和-3来说,存放-4时为:<62,97>,存放-3时为:<62,98>。因此在比较-4和-3谁大时,可以看到98>97,因此-3>-4。

基于这样的比较方法,所以oracle会在数值最后存放102,也就是0x66,来表示它们都是负数。可以考虑下面的情况:-100 <61,100,102>和-115 <61, 100, 86, 102>。如果没有最后一位的102,则它们在比较大小时就要发生错误了。

因为digit基于100而来,一个字节可以表示的最大的数值为99。而exponent的范围从-65到62。因此,oracle能够保留的正数的最小值为:1 x 100^(-65) = 1 x 10^(-130)

而最大值为:99 x 100^(62) + 99 x 100^(61) + 99 x 100^(60) + ... + 99 x 100^(0),大约为1 x 10^(126)。