淺析number型別的值

hanson發表於2019-04-14
number資料內部儲存時,以變長的陣列來存放,陣列裡的每個元素佔一個位元組,最多20個元素。內部程式碼為2。number資料的存放格式為:,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)。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/9842/viewspace-353137/,如需轉載,請註明出處,否則將追究法律責任。

相關文章