注册 登录
编程论坛 VFP论坛

关于浮点数转换的问题,请大侠指教

Pgwyg 发布于 2023-08-27 15:26, 750 次点击
本人使用VFP做上位机开发了一个远程抄表程序,终端设备使用 MODBUS RTU 协议,数据为浮点型,于是编写了一个10进制与16进制浮点型数据转换函数,但是转换结果不知正确与否,例如,10进制数22.5转换16进制浮点数为41B46660,而这个16进制数在还原回去为22.5499877929687500,不知道是否正确,请大侠指点。
附两个函数,不打包了,直接复制粘贴到VFP即可使用。


*浮点型数据十进制转换十六进制浮点型数据
*依据IEEE-754标准
*八位HEX数据转换成32位二进制数据后从高位至低位分为3个部分
*S-第1位,符号位,0为正数,1为负数
*E-第2至9位,指数位,浮点的位置,数值减去127所得
*M-第10至32位,数据位,由整数和小数组成
*函数名:FDX_DECTOHEX
*用法:FDX_DECTOHEX(浮点数值) &&数值型,返回字符型
Function FDX_DECtoHEX
LPARAMETER DEC_SJ

*1-判别符号+/-S
IF DEC_SJ>0
    S="0"
ELSE
    S="1"
    DEC_SJ=ABS(DEC_SJ)
ENDIF

*分解数据为整数和小数
*整数部分
SJ1=INT(DEC_SJ)
*小数部分
SJ2=DEC_SJ-SJ1

*整数部分转二进制,除2取余
FOR I=1 TO 32
     X1=SJ1/2
    X2="ZS"+ALLTRIM(STR(I))
    &X2=SJ1%2
    SJ1=INT(X1)
    IF SJ1=0
        EXIT
    ENDIF
ENDFOR
X2=I
X3=I
X4=""
FOR I=1 TO X2
    X5="ZS"+ALLTRIM(STR(X3))
    X4=X4+ALLTRIM(STR(&X5))
    X3=X3-1
ENDFOR
ZSBF=X4
X2=""
*小数部分转二进制,乘2取整
FOR I=1 TO 16
    X1="XS"+ALLTRIM(STR(I))
    SJ2=SJ2*2
    X2=X2+ALLTRIM(STR(INT(SJ2)))   
    IF SJ2>0
        SJ2=SJ2-INT(SJ2)
    ENDIF
ENDFOR   
XSBF=X2
M=SUBSTR(ZSBF,2)+XSBF

*计算指数E
E1=LEN(ZSBF)-1+127
SJ1=E1
FOR I=1 TO 32
     X1=SJ1/2
    X2="ZS"+ALLTRIM(STR(I))
    &X2=SJ1%2
    SJ1=INT(X1)
    IF SJ1=0
        EXIT
    ENDIF
ENDFOR

X2=I
X3=I
X4=""
FOR I=1 TO X2
    X5="ZS"+ALLTRIM(STR(X3))
    X4=X4+ALLTRIM(STR(&X5))
    X3=X3-1
ENDFOR
E=X4

M=SUBSTR(ZSBF,2)+XSBF
M=PADR(M,23,"0")
FDSJ2=S+E+M
FDSJ16=""
XX=1

*二进制转十六进制
FOR I=1 TO 8
    SJ16=SUBSTR(FDSJ2,XX,4)
    DO CASE
    CASE SJ16="0000"
        FDSJ16S="0"
    CASE SJ16="0001"
        FDSJ16S="1"
    CASE SJ16="0010"
        FDSJ16S="2"
    CASE SJ16="0011"
        FDSJ16S="3"
    CASE SJ16="0100"
        FDSJ16S="4"
    CASE SJ16="0101"
        FDSJ16S="5"
    CASE SJ16="0110"
        FDSJ16S="6"
    CASE SJ16="0111"
        FDSJ16S="7"
    CASE SJ16="1000"
        FDSJ16S="8"
    CASE SJ16="1001"
        FDSJ16S="9"
    CASE SJ16="1010"
        FDSJ16S="A"
    CASE SJ16="1011"
        FDSJ16S="B"
    CASE SJ16="1100"
        FDSJ16S="C"
    CASE SJ16="1101"
        FDSJ16S="D"
    CASE SJ16="1110"
        FDSJ16S="E"
    CASE SJ16="1111"
        FDSJ16S="F"
    ENDCASE
    FDSJ16=FDSJ16+    FDSJ16S
    XX=XX+4
ENDFOR

*返回计算结果
RETURN FDSJ16
ENDFUNC

*****************************************************
*函数名:FDX_HEXTODEC
*用法:FDX_HECTODEC("8位16进制数") &&字符型,返回数值型

Function FDX_HEXTODEC

LPARAMETER HEX_SJ
FDSJ=0
B1=LEN(ALLTRIM(HEX_SJ))

IF B1 <>8
RETURN FDSJ
ENDIF
HEX_SJ=UPPER(HEX_SJ)
* 16进制转换2进制

HEX_SJ_2=""
FOR i=1 TO b1
    f_x1=SUBSTR(HEX_SJ,i,1)
    DO CASE
        CASE f_x1="0"
        x="0000"
        CASE f_x1="1"
        x="0001"
        CASE f_x1="2"
        x="0010"
        CASE f_x1="3"
        x="0011"
        CASE f_x1="4"
        x="0100"
        CASE f_x1="5"
        x="0101"
        CASE f_x1="6"
        x="0110"
        CASE f_x1="7"
        x="0111"
        CASE f_x1="8"
        x="1000"
        CASE f_x1="9"
        x="1001"
        CASE f_x1="A"
        x="1010"
        CASE f_x1="B"
        x="1011"
        CASE f_x1="C"
        x="1100"
        CASE f_x1="D"
        x="1101"
        CASE f_x1="E"
        x="1110"
        CASE f_x1="F"
        x="1111"
    ENDCASE
    HEX_SJ_2=HEX_SJ_2+x
ENDFOR

SJ=SUBSTR(HEX_SJ_2,10)&&符号位,0为正数
DW=SUBSTR(HEX_SJ_2,2,8)&&指数位,小数点位置
FH=VAL(SUBSTR(HEX_SJ_2,1,1))&&数据位
*计算指数
a0=0
a1=DW
a2=LEN(a1)
FOR i=0 TO a2-1
    a3=SUBSTR(a1,a2-i,1)
    a4=VAL(a3)*2^i
    a0=a0+a4
ENDFOR
ZS=A0-127

*计算数据
a0=0
a1=SJ
a2=LEN(a1)
X=0
FOR i=1 TO a2
    x=X-1
    a3=SUBSTR(a1,i,1)
    a4=VAL(a3)*2^x
    a0=a0+a4

ENDFOR

FDSJ=(-1^FH)*(1+A0)*2^ZS
RETURN FDSJ
ENDFUNC
***************************************************************************************
11 回复
#2
csyx2023-08-27 16:02
何苦要自己发明轮子

cc = BinToC(22.5,'fr')
? 0h+cc
? CToBin(cc,'nr')
#3
iswith2023-08-27 16:10
以下是引用csyx在2023-8-27 16:02:28的发言:

何苦要自己发明轮子

cc = BinToC(22.5,'fr')
? 0h+cc
? CToBin(cc,'nr')

我以前也是这么干的。。。。。呵呵。。。
#4
Pgwyg2023-08-27 16:25
不知道VFP有自己的函数,领教了,谢谢!
#5
csyx2023-08-27 16:39
如果是研究算法实现也就无可厚非,关键你的 DECtoHEX 有瑕疵
只有本站会员才能查看附件,请 登录
#6
foxfans2023-08-27 17:27


[此贴子已经被作者于2023-8-27 18:20编辑过]

#7
Pgwyg2023-08-27 17:37
回复 5楼 csyx
这一点我不知如何操作,读入的数据是字符型,例如“41cc0000”,怎样用CToBin()函数取值?CToBin(0h4cc40000,'nr')的结果是正确的,但是“41cc0000”字符串如何转换成数值0h41cc0000?谢谢!
#8
foxfans2023-08-27 17:46
CToBin(STRCONV("41cc0000",16),'nr')
#9
tigerpub2023-08-27 17:55
回复 6楼 foxfans
#10
tigerpub2023-08-27 17:56
回复 8楼 foxfans
大佬。
#11
Pgwyg2023-08-27 18:04
大佬就是大佬!自己写的函数精度不够,还是VFP原生函数好。
#12
csyx2023-08-27 18:23
以下是引用Pgwyg在2023-8-27 17:37:03的发言:

这一点我不知如何操作,读入的数据是字符型,例如“41cc0000”,怎样用CToBin()函数取值?CToBin(0h4cc40000,'nr')的结果是正确的,但是“41cc0000”字符串如何转换成数值0h41cc0000?谢谢!


cc = '41cc0000'
? CToBin(Strconv(cc, 16), 'NR')
? CToBin(Evaluate('0h'+cc), 'NR')
1