在從事大型機的工作中,很多時候我們所做的都是讀dump,然後反向找出VSAM/QSAM數據集中的不當記錄,尤其當數據集很大的時候,精確定位一條記錄很是不便。。。而這裏介紹的利用浮點數反向查找就是一個捷徑(比如:我們在dump裏看到X'C411570A',如果能快速算出-4439.039,再在數據集中搜索-4439.039,相信很容易就能鎖定到你想要的目標記錄),下面我們解析下,大型機是如何存儲浮點數及我們又怎樣快速算出其對應的十進制數據。
我們知道在COBOL裏:
COMP-1是用來定義單精度浮點數的,佔四個字節(對應於HLASM/mainframe assembler裏的E定義)
COMP-2是用來定義雙精度浮點數的,佔八個字節(對應於HLASM/mainframe assembler裏的D定義)
具體內部存儲見下圖:
解析如下:
第0位(最左邊的bit位爲第0位,以此類推)是符號位,0爲正,1爲負。
第1—7位是指數位,初始值爲X'40',如果小數點左移1位,則初始值加1,爲X'41';如果右移1位,則減1,爲X'3F'。
第8—31/8—63位是尾數位,也可以說是組合位(8—31是針對COMP-1,8—63針對COMP-2)。
1.主機是以純小數並且十分位不爲0的形式存儲數據的
地址 內存數據 常量名 常量數值
000064 427B74BC 14 A DC E'123.456'
000068 427B0000 15 B DC E'123'
00006C 4074BC6A 16 C DC E'.456'
000070 C27B74BC 17 X DC E'-123.456'
000074 00000000
000078 427B74BC6A7EF9DB 18 Y DC D'123.456'
A,B和C我們都定義成常量,並且可以把A看成是B + C之和
B: 123 = X'7B'按照要求應該變爲0.7B * 16^2,由於指數爲+2,所以指數的bit位就變成了X'40'+X'2'=X'42',即內存爲427B0000
C: 0.456=X'0.74BC6A7EF9DB'=0.74BC6A7EF9DB *
16^0,其本身已經符合要求了,所以內存爲4074BC6A(由於C最多四個字節,後面的存儲不了自動捨棄,這和我們常說的小說點後精確到多少位含義是一樣)
計算機在進行加減之前,首先要保證指數一致,然後再保證小數點對齊。要理解這個,我們可以參考下十進制,比如100*10^1+ 0.008*10^2,一般的算法爲:100*10^1+ 0.008*10^2 = 1000 + 0.8 = 1000.8
其實這裏隱含的約束也是先把指數變成一樣,然後小數點對齊(只是這裏指數部分爲10^0)。
其實計算機也是這麼做的:
1.先把前兩位(指數位)變成相同:4074BC6A -> 41074BC6 -> 420074BC
2.然後再進行C+B=A的計算:420074BC+ 427B0000 = 427B74BC
再來看下X和Y,X是A的相反數,Y和A只是精度不同
X是負數,所以其內存第一個bit位是1。
Y被定義成雙精度浮點數類型(COMP-2),可以看出A和Y的內存數據僅僅是精度不同而已。
2.剖析一個內存數據,比如:C211570A
1.由於第一個bit是1,所以它是個負數,
2.由於指數位是X'42',即後面三個字節中的11爲整數部分,570A爲小數部分
3.整數部分11變成十進制爲17
4.小數部分0.570A變成十進制爲0.339996337890625,約等於0.34
(這裏給個百度鏈接,十進制/十六進制轉換工具:http://www.baidu.com/s?tn=baiduhome_pg&ie=utf-8&bs=Fraction&f=8&rsv_bp=1&rsv_spt=1&wd=01+%E5%8D%81%E5%85%AD%E8%BF%9B%E5%88%B6%E8%A1%A8%E7%A4%BA&rsv_sug3=9&rsv_sug=0&rsv_sug1=9&rsv_sug4=688&inputT=10402)
5.即這個數字爲-17.34
3.怎麼從一個十六進制數據快速反推出我們想要的十進制
1)快速反推(由十六進制推算出十進制):
C211570A =(-X'11570A'/16^6)*16^2
= -X'11570A'/16^4
= -1136394/65536
= -17.34
C411570A = (-X'11570A'/16^6)* 16^4
= -X'11570A'/16^2
= -1136394/256
= -4439.039
2)快速正推(由十進制推算出十六進制):
17.34 = 0.1734 * 10^2
= 1734/10^2
= X'6C6'/ X'64'
= (X'6C60000'/ X'64')*(1/X'10000')
= 11570A*(1/ X'10000')
這裏爲什麼先讓被除數乘X'10000',是因爲筆記本自帶的計算機在進行16進制除法的時候,只顯示整數部分.實際上,計算機也是沒法表示16進制的小數部分的,因爲我們平時說的小數點,都是針對十進制的,又有誰見過16進制的小數點長什麼樣子?
所以記入內存時再縮小X'10000'倍即可:X'4211570A'(如果不縮小應該是X'4611570A')
以上方法的總體思想都是把除數和被除數都變成整數,然後再進行運算。
有疑問請聯繫:QQ349106216