在从事大型机的工作中,很多时候我们所做的都是读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