|
本帖最后由 qingyun 于 2017-1-29 18:50 编辑
其实是项目中用到,称重机设备传过来的是4个字节的单精度数;
解析是做在存储过程里的; 如果解析在前台做,用前台语言,非常方便能处理了;
但是用sql,却发现真的没有现成的函数;被迫无奈,只能动手自己写了;
参考了:
https://www.h-schmidt.net/FloatConverter/IEEE754.html
里面可以做实验验证;
大致思路是:
1. 把这4个字节转换成32个2进制数,形式:
1位 8位 23位
X xxxxxxxx XXXXXXXXXXXXXXXXXXXXXXX
符号 阶 尾数
翻译结果是:
符号* ( POWER(2, 阶-127) + 0.尾数*POWER(2,阶-127);
为什么这么设计,估算是这样设计最符合 cpu的思维,运算速度最快;
程序如下:
- CREATE OR REPLACE FUNCTION HEX_TO_FLOAT(P_HEX VARCHAR2) RETURN binary_float IS
- V_HEX VARCHAR2(8); --4个字节
- V_BIN VARCHAR2(32); --32个2进制数
- V_SIGN VARCHAR2(1); --正数或负数的标记;
- V_RESULT binary_float;
- V_Exponent PLS_INTEGER; --阶;
- V_Mantissa binary_float; --位数;
- V_LEN pls_integer;
- BEGIN
- BEGIN
- V_HEX := UTL_RAW.REVERSE(P_HEX); --解析的方式 :从低到高; 但是输入的顺序是:从高到低;
- EXCEPTION
- WHEN OTHERS THEN
- RETURN - 1; --无效的;
- END;
- --把4个字节转换为32位的2进制;
- SELECT LISTAGG(data) WITHIN GROUP(order by rownum)
- INTO V_BIN
- FROM (SELECT (CASE upper(substr(V_HEX, rownum, 1))
- WHEN '0' THEN
- '0000'
- WHEN '1' THEN
- '0001'
- WHEN '2' THEN
- '0010'
- WHEN '3' THEN
- '0011'
- WHEN '4' THEN
- '0100'
- WHEN '5' THEN
- '0101'
- WHEN '6' THEN
- '0110'
- WHEN '7' THEN
- '0111'
- WHEN '8' THEN
- '1000'
- WHEN '9' THEN
- '1001'
- WHEN 'A' THEN
- '1010'
- WHEN 'B' THEN
- '1011'
- WHEN 'C' THEN
- '1100'
- WHEN 'D' THEN
- '1101'
- WHEN 'E' THEN
- '1110'
- WHEN 'F' THEN
- '1111'
- END) data
- FROM dual
- CONNECT BY rownum <= 8);
- V_SIGN:=SUBSTR(V_BIN,1,1); --获取正数或负数符号; 0是正数 1是负数
- --获取第2~9这8个数的10进制数值;
- SELECT SUM(data1)
- into V_Exponent
- FROM (SELECT substr(substr(V_BIN, 2, 8), rownum, 1) *
- power(2, 8 - rownum) data1
- FROM dual
- CONNECT BY rownum <= 8);
- --数值减去127 就是阶;
- V_Exponent := V_Exponent - 127;
- V_BIN := RTRIM(substr(V_BIN, 10), '0'); --尾数部分;
- --尾数的长度
- v_len:=length(v_bin);
- --计算尾数的值;
- V_Mantissa:=0;
- for i in 1..v_len
- loop
- IF SUBSTR(v_bin,I,1)='1'
- THEN
- V_Mantissa:=V_Mantissa+power(2,-i+V_Exponent);
- END IF;
- end loop;
- --计算真实的值;
- V_RESULT:= POWER(2, V_Exponent)+ V_Mantissa;
- if V_SIGN='1'
- then
- V_RESULT:=-V_RESULT;
- end if;
- return V_RESULT;
- EXCEPTION
- WHEN OTHERS THEN
- RETURN - 1;
- END;
复制代码
不过发现 oracle 的存储方式和标准的IEEE不太一样;
比如:
select dump(cast(1.77 as binary_float),16) from dual;
------------------
Typ=100 Len=4: bf,e2,8f,5c ( 这个的bf应该是3f);
这个结果按标准的ieee方法,应该是-1.77;
但是如果真的是解析:-1.77
select dump(cast(-1.77 as binary_float),16) from dual;
------------------
Typ=100 Len=4: 40,1d,70,a3
发现正负相同的数,解析结果又大相径庭;
不管这些了,总之问题解决了;
|
|