本程序是根据 陈远征的浮点数变为压缩BCD码 FOR 51 源代码移植过来的,并做了修正。
全部是用汇编写,可提供C函数调用,有了这个函数,浮点数转字符串就可以不用sprintf了,无论占用空间或时间都优于sprintf甚多。
功能:将符合IEEE574标准的浮点数变为压缩BCD码,保存在以数组 CYZ 中
第1字节的位7:0正,1负.位6:0(位5--0代表小数点前的位数),1(位5--0代表小数点后0的位数)
2--4字节为压缩BCD码,有效位为7位,3个半字节,最后半个字节请使用者自行放弃
SP深度6,RAM 5个放数据
调用了两个数学库函数:1、浮点数乘法 2、无符号字节除法(AVR没有除法指令啊)
IAR中的浮点数形参传递规则:
R19 R18 R17 R16
参数传递在R19--R16中,浮点数IEEE标准seeeeeee emmmmmmm mmmmmmmm mmmmmmmm
主要思路:
把IEEE574标准的浮点数进行放大10^10(10的10次方)倍(浮点运算),使其≤10^10和≥10^0(即 1),再查表找出他的数量级,再除以数量级(浮点运算)得到二进制的纯小数(还是IEEE574标准),再进行规格化变为二进制纯小数,尾数乘以100得到整数部分就是十分位和百分位,再十分位和百分位得到整数部分就是千分位和万分位....,再每次乘以100得到整数转换为BCD码,最后进行四舍五入,存档即可。
用到的知识:
1、浮点数IEEE标准
2、浮点数比较
3、查表
4、浮点数标准化
5、多字节乘法
6、BCD码加法
7、IAR的参数传递规则
主要修正:在陈远征的51程序中,多字节乘法没有考虑字节间进位(乘以100时),因此,有个别数转换误差较大,比如1.002,他转换为1.001609
我的这个版本中已经修复了这个问题。
欢迎测试和使用。
源代码:
;------------------------------------FloatToBCD.s90文件-----------------------
NAME FloatToBCD
#include <ioavr.h>
PUBLIC FloatToBCD
EXTERN ?F_MUL_L04
EXTERN ?UC_DIVMOD_L01
EXTERN CYZ
RSEG CODE:CODE
;程序说明:浮点数变为压缩BCD码,保存在以数组 CYZ 中
; 第1字节的位7:0正,1负.位6:0(位5--0代表小数点前的位数),1(位5--0代表小数点后0的位数)
; 2--4字节为压缩BCD码,有效位为7位,3个半字节,最后半个字节请使用者自行放弃
; SP深度6,RAM 5个放数据
;参数传递在R19--R16中,浮点数IEEE标准seeeeeee emmmmmmm mmmmmmmm mmmmmmmm
FloatToBCD:
;保存符号位
MOV R24, R19
ANDI R24, 0x80 ;CYZ.7
STS CYZ, R24
;取绝对值
MOV R24, R19
ANDI R24, 0x7F
MOV R19, R24
OR R24, R18 ;判断是否为0
TST R24
BRNE FCMP_1E10 ;不为0,跳转
;待转换浮点数为0时
LDI R24, 0
STS CYZ, R24
LDI R24, 0
STS (CYZ+1), R24
LDI R24, 0
STS (CYZ+2), R24
LDI R24, 0
STS (CYZ+3), R24
LDI R24, 0
STS (CYZ+4), R24
;转换完成,程序返回
RET
FCMP_1E10:
LDI ZL, LOW(DE10)
LDI ZH, HIGH(DE10)
CALL FR0DPTR ;数值 10^10 装入R23--R20
CALL FCMP ;与 10^10 比较
BRCC FCMP_1E0 ;判断C,如C=0,则数值小于等于 10^10,跳转
LDI ZL, LOW(DE_10)
LDI ZH, HIGH(DE_10) ;数值大于 10^10,X=X*(10^-10)使数值缩小 10^10 倍
CALL FR0DPTR ;将数值 10^10 装入 R23--R20
CALL ?F_MUL_L04 ;浮点数乘法,结果在 R19--R16
LDS R24, CYZ
CBR R24, 0x40 ;CYZ.6=0,不为纯小数
SUBI R24, 246 ;加10
STS CYZ, R24
JMP FCMP_1E10
FCMP_1E0:
LDI ZL, LOW(DE0)
LDI ZH, HIGH(DE0)
CALL FR0DPTR ;数值 10^0 装入R23--R20
CALL FCMP ;与 10^0 比较
BRCS FCMP_1E0_10 ;判断C,如C=1,则数值大于 10^0,跳转
BREQ FCMP_1E0_10 ;判断Z,如Z=1,则数值等于 10^0, 跳转
LDI ZL, LOW(DE10)
LDI ZH, HIGH(DE10) ;数值小于 10^0, X=X*(10^10)使数值放大 10^10 倍
CALL FR0DPTR ;数值10^10装入 R23--R20
CALL ?F_MUL_L04 ;浮点数乘法,结果在 R19--R16
LDS R24, CYZ
SBR R24, 0x40 ;CYZ.6=1,为纯小数
SUBI R24, 246 ;加10
STS CYZ, R24
JMP FCMP_1E10
FCMP_1E0_10: ;转换为纯小数
LDI ZL, LOW(DE0)
LDI ZH, HIGH(DE0)
FCMP_FIND:
CALL FR0DPTR
CALL FCMP
BRNE FCMP_UNEQU ;不相等,跳转
LDI R24, 0x10 ;正好是表格中的数,尾数为0.10000000
STS (CYZ+1), R24
LDI R24, 0
STS (CYZ+2), R24
LDI R24, 0
STS (CYZ+3), R24
LDI R24, 0
STS (CYZ+4), R24
CALL CYZDEAL_JIE ;调整幂值调整
;转换完成,程序返回
RET
FCMP_UNEQU:
BRCS FCMP_NEXT ;待转换浮点数大,跳转
;查表,找到一个比待转换浮点数大的整数幂 10^+e ,再将比待转换浮点数乘以 10^-e 得到纯小数
;地址计算:A(10^-e)=A(10^0)-[A(10^+e)-A(10^0)],注:A(..)意为..的地址
; ZH:ZL-(R27:R26-ZH:ZL),完成后,Z 还有加 4 才能得到 10^-e 的首地址
MOVW R26, ZL
LDI ZL, LOW(DE0)
LDI ZH, HIGH(DE0)
CLC
SUB R26, ZL
SBC R27, ZH
CLC
SUB ZL, R26
SBC ZH, R27
ADIW ZL, 4 ;Z=Z+4
CALL FR0DPTR ;数值 10^-e 装入R23--R20
CALL ?F_MUL_L04 ;浮点数乘法,结果在 R19--R16
JMP YUANZHENG_FBCD ;得到一个二进制浮点数的纯小数。
FCMP_NEXT:
LDS R24, CYZ ;幂值调整
BST R24, 6
BRTS FCMP_NEXT1 ;CYZ.6为1,跳转
LDS R24, CYZ
INC R24 ;加1
STS CYZ, R24
JMP FCMP_FIND
FCMP_NEXT1:
LDS R24, CYZ
DEC R24 ;减1
STS CYZ, R24
JMP FCMP_FIND
YUANZHENG_FBCD:
MOV R24, R18 ;恢复阶码
ROL R24 ;取R18的最高位放到C中
ROL R19 ;R19循环左移,把C放到R19的最低位
SUBI R19, 126 ;-126
ORI R18, 0x80 ;恢复尾数
CYZFTB0:
TST R19 ;取阶码
BREQ CYZFTB1 ;为零,跳转
CLC
CALL RR1 ;右规。
RJMP CYZFTB0
CYZFTB1:
CALL HB2 ;转换尾数的十分位和百分位
STS (CYZ+1), R24
CALL HB2 ;转换尾数的千分位和万分位
STS (CYZ+2), R24
CALL HB2 ;转换尾数的十万分位和百万分位
STS (CYZ+3), R24
CALL HB2 ;转换尾数的千万分位和亿分位
STS (CYZ+4), R24
CLC
LDI R24, 0x05 ;此处后半字节(BCD码的第8位)的值不保证
LDS R26, CYZ+4
CALL ADDAA ;BCD码相加后调整
STS CYZ+4, R24
CLR R24
LDS R26, CYZ+3
CALL ADDAA
STS CYZ+3, R24
CLR R24
LDS R26, CYZ+2
CALL ADDAA
STS CYZ+2, R24
CLR R24
LDS R26, CYZ+1
CALL ADDAA
STS CYZ+1, R24
BRCC CYZFTB2
LDI R25, 0x10
STS CYZ+1, R25
CALL CYZDEAL_JIE ;幂值调整
CYZFTB2: ;转换完成,程序返回
RET
CYZDEAL_JIE:
LDS R24, CYZ ;幂值调整
BST R24, 6
BRTC CYZDEAL_JIE1
DEC R24
STS CYZ, R24
RET
CYZDEAL_JIE1:
INC R24
STS CYZ, R24
RET
RR1:
MOV R24, R18 ;第一操作数右规一次
ROR R24 ;尾数缩小一半
MOV R18, R24
MOV R24, R17
ROR R24
MOV R17, R24
MOV R24, R16
ROR R24
MOV R16, R24
INC R19 ;阶码加一
CLV ;清溢出标志
CPI R19, 0x80
BRNE RR1E ;阶码上溢否?
LDI R19, 0x7F ;阶码溢出
SEV
RR1E:
RET
HB2:
;尾数扩大100倍
LDI R25, 100
MUL R16, R25 ;R16*100
MOV R16, R0 ;(R16*100)L -> R16
MOV R24, R17
MOV R17, R1 ;(R16*100)H -> R17
MUL R24, R25 ;R17*100
ADD R17, R0 ;(R17*100)L + (R16*100)H -> R17,进位放到 C 中
MOV R24, R18
CLR R18
ADC R18, R1 ;(R17*100)H + C -> R18,加上进位
MUL R24, R25 ;R18*100
ADD R18, R0 ;(R18*100)L + (R17*100)H + C -> R18,进位放到 C 中
MOV R25, R16 ;备份R16
CLR R16
ADC R16, R1 ;(R18*100)H + C -> R16,加上进位,R16为整数部分
;将整数部分转换成BCD码
LDI R20, 10
CALL ?UC_DIVMOD_L01 ;无符号字节除法,R16/R20,商放到 R16,余数放到 R20
SWAP R16
OR R16, R20
MOV R24, R16 ;结果 -> R24
MOV R16, R25 ;恢复R16
RET
ADDAA:
ADC R24, R26 ;BCD码加法子程序
IN R26, SREG ;先保存相加后的SREG
LDI R27, 0x66 ;
ADD R24, R27 ;将和预加立即数0x66
IN R27, SREG ;输入相加后的SREG
OR R26, R27 ;新旧状态相或
SBRS R26, 0 ;相或后进位置位则跳行
SUBI R24, 0x60 ;否则减去$60(十位bcd不满足调整条件)
SBRS R26, 5 ;半进位置位则跳行
SUBI R24, 6 ;否则减去$06(个位bcd不满足调整条件)
RET
FR0DPTR: ;装入R23--R20
LPM R23, Z+
LPM R22, Z+
LPM R21, Z+
LPM R20, Z+
RET
FCMP: ;比较两个正的浮点数
CLC
MOV R24, R23
SUB R24, R19
BRNE FCMP1 ;不相等跳转,再通过判断C标志,便可分出大小
MOV R24, R22
SUB R24, R18
BRNE FCMP1
MOV R24, R21
SUB R24, R17
BRNE FCMP1
MOV R24, R20
SBC R24, R16
FCMP1:
RET
RSEG CODE:CODE
DE_10:
DB 02EH,0DBH,0E6H,0FFH ;10^-10
DB 030H,089H,070H,05FH ;10^-9
DB 032H,02BH,0CCH,077H ;10^-8
DB 033H,0D6H,0BFH,095H ;10^-7
DB 035H,086H,037H,0BDH ;10^-6
DB 037H,027H,0C5H,0ACH ;10^-5
DB 038H,0D1H,0B7H,017H ;10^-4
DB 03AH,083H,012H,06FH ;10^-3
DB 03CH,023H,0D7H,00AH ;10^-2
DB 03DH,0CCH,0CCH,0CDH ;10^-1
DE0:
DB 03FH,080H,000H,000H ;10^0
DB 041H,020H,000H,000H ;10^1
DB 042H,0C8H,000H,000H ;10^2
DB 044H,07AH,000H,000H ;10^3
DB 046H,01CH,040H,000H ;10^4
DB 047H,0C3H,050H,000H ;10^5
DB 049H,074H,024H,000H ;10^6
DB 04BH,018H,096H,080H ;10^7
DB 04CH,0BEH,0BCH,020H ;10^8
DB 04EH,06EH,06BH,028H ;10^9
DE10:
DB 050H,015H,002H,0F9H ;10^10
END
//--------------------main.c-------------------------------------
#include <ioavr.h>
#include <ina90.h>
extern void FloatToBCD(float i);//声明为外部函数
unsigned char CYZ[5]; //定义接收数组
void main()
{
while(1)
{
FloatToBCD(1.3992928);
}
}
//1.3992928 的结果 01 13 99 29 28 //第1字节 01 第7和6位均为0,5到0为的数值1,表示为正数,小数点前有1位整数
//1.002 的结果 01 10 02 00 04
//-0.00123 的结果 C2 12 30 00 01 //第1字节 C2 第7和6位均为1,5到0为的数值2,表示为负数,纯小数点后有2个0
点击此处下载ourdev_211017.rar(文件大小:12K) |