一个速度较快的 int 数转换为BCD的程序【恢复】
在LED显示或者LCD显示中,经常要用到字符的显示,这时就要把数值转换为BCD数。经常用到的是除法以及求余,这个方法太慢,为了一个显示,把整个程序的速度拖得很慢是不是不划算。另外网上也有51的汇编代码,但是本人不太用汇编, C语言来得直观方便。unsigned int t;
char buffer={0,0,0,0,0};
void HexToBCD(unsigned int t)
{
char i=0;
unsigned int j=0;
for(i=0,j=0;j<t-10000;i++,j+=10000)
{}
buffer=i; //万位
t=t-j;
for(i=0,j=0;j<t-1000;i++,j+=1000)
{}
buffer=i; //千位
t=t-j;
for(i=0,j=0;j<t-100;i++,j+=100)
{}
buffer=i; //百位
t=t-j;
for(i=0,j=0;j<t-10;i++,j+=10)
{}
buffer=i; //十位
buffer=t-j; //个位
}
void main()
{
unsigned int x;
x=45535;
HexToBCD(x);
while(1);
}
点击此处下载 ourdev_505534.rar(文件大小:10K) (原文件名:HexToBCD.rar)
本贴被 dd123 编辑过,最后修改时间:2008-11-20,16:55:32. 有创意,顶一个 不是我打击楼主,其实你用了unsigned int j,在获取万千百十个位时候,有可能进行多次加法,并且还要使用unsigned int 数据进行判断,速度也不是快很多,不信可以调试看看。如果是unsigned char类型数据则令当别论。 1.同时求余数和商,应该使用除法库函数,这样只算一次除法。
不要直接用/和%,这会算两次除法。
2.每算一次除法,除法位数减半。
比如要将一个16进制数(用16位2进制数表示)转换成4位10进制数,
第一步,一次16位除法,得到10进制数高两位位和低两位,
第二步,两次8位除法, 得到十进制数全部8位。
共:一次16位除法,两次8位除法。
另:
外关于除法库函数,标准C语言只提供了三个,div和ldiv,和lldiv.都是有符号除法。
对于AVRGCC,实际上8位,16位,32位,64位有符号,无符号除法库函数都有。
只是非标准C要求的除法库函数没有显式包含在头文件里。
本贴被 ATmega32 编辑过,最后修改时间:2008-11-21,15:20:42. 用减法代替除法。 还是查表最快 mark 真的需要快,就查表,如果没有那种要求,就算吧,别计较太多了 回【2楼】 lzf713
请教用unsigned char 实现无符号整型的转换方法。
回【3楼】 ATmega32 上官金虹
在8位机里面,多次用除法和乘法对转换速度的影响是不是很大呢?我的原则是,能不用乘除法时则尽量不用乘除法。
回【7楼】 avenbbs
如果用查表的方法,对于65535这样的数,需要建一个多大的表?
真诚请教与探讨。 【3楼】 ATmega32 上官金虹
1.同时求余数和商,应该使用除法库函数,这样只算一次除法。
不要直接用/和%,这会算两次除法。
对于AVRGCC,实际上8位,16位,32位,64位有符号,无符号除法库函数都有。
只是非标准C要求的除法库函数没有显式包含在头文件里。
=====================================================================
上官你好:这个具体如何来做?
我现在是用/和%来实现的,估计会算两次除法。
比如这段程序:
uint temp1= 1234;
a = temp1/1000; //千位
b = (temp%1000)/100; //百位
c = (temp%100)/10 ; //十位
d = temp1%10 ; //个位
用你的方法,应该可以更简化吧。 我也象楼上这样用的 #include <stdlib.h>
#include <stdint.h>
typedef struct udiv8_t
{
uint8_t quot;
uint8_t rem;
}udiv8_t;
typedef struct div8_t
{
int8_t quot;
int8_t rem;
}div8_t;
typedef struct udiv16_t
{uint16_t quot;
uint16_t rem;
}udiv16_t;
#define div16_t div_t
typedef struct udiv32_t
{
uint32_t quot;
uint32_t rem;
}udiv32_t;
#define div32_t ldiv_t
extern div8_t div8(int8_t,int8_t) __asm__("__divmodqi4") __ATTR_CONST__ ;
extern udiv8_t udiv8(uint8_t,uint8_t) __asm__("__udivmodqi4") __ATTR_CONST__ ;
extern div16_t div16(int16_t,int16_t) __asm__("__divmodhi4") __ATTR_CONST__ ;
extern udiv16_t udiv16(uint16_t,uint16_t) __asm__("__udivmodhi4") __ATTR_CONST__ ;
extern div32_t div32(int32_t,int32_t) __asm__("__divmodsi4") __ATTR_CONST__ ;
extern udiv32_t udiv32(uint32_t,uint32_t) __asm__("__udivmodsi4") __ATTR_CONST__ ;
unsigned char data ;
int main()
{
unsigned int temp= 1234;
udiv16_t qr16;
udiv8_t qr8;
qr16=udiv16(temp,100);
qr8=udiv8(qr16.quot,10);
data=qr8.quot;
data=qr8.rem;
qr8=udiv8(qr16.rem,10);
data=qr8.quot;
data=qr8.rem;
while(1);
}
int main()
{
92: 1f 93 push r17
unsigned int temp= 1234;
udiv16_t qr16;
udiv8_t qr8;
qr16=udiv16(temp,100);
94: 64 e6 ldi r22, 0x64 ; 100
96: 70 e0 ldi r23, 0x00 ; 0
98: 82 ed ldi r24, 0xD2 ; 210
9a: 94 e0 ldi r25, 0x04 ; 4
9c: 0e 94 6f 00 call 0xde ; 0xde <__udivmodhi4> //第一次除法,16位无符号
a0: 26 2f mov r18, r22
a2: 18 2f mov r17, r24
qr8=udiv8(qr16.quot,10);
a4: 6a e0 ldi r22, 0x0A ; 10
a6: 82 2f mov r24, r18
a8: 0e 94 63 00 call 0xc6 ; 0xc6 <__udivmodqi4> //第二次除法,8位无符号
data=qr8.quot;
ac: 80 93 63 00 sts 0x0063, r24
data=qr8.rem;
b0: 90 93 62 00 sts 0x0062, r25
qr8=udiv8(qr16.rem,10);
b4: 6a e0 ldi r22, 0x0A ; 10
b6: 81 2f mov r24, r17
b8: 0e 94 63 00 call 0xc6 ; 0xc6 <__udivmodqi4> //第三次除法,8位无符号
data=qr8.quot;
bc: 80 93 61 00 sts 0x0061, r24
data=qr8.rem;
c0: 90 93 60 00 sts 0x0060, r25
c4: ff cf rjmp .-2 ; 0xc4 <main+0x32> unsigned char data ;
unsigned int temp= 1234;
int main()
{
data = temp/1000; //千位
data = (temp%1000)/100; //百位
data = (temp%100)/10 ; //十位
data = temp%10 ; //个位
while(1);
}
int main()
{
92: 20 91 60 00 lds r18, 0x0060
96: 30 91 61 00 lds r19, 0x0061
9a: c9 01 movw r24, r18
9c: 68 ee ldi r22, 0xE8 ; 232
9e: 73 e0 ldi r23, 0x03 ; 3
a0: 0e 94 72 00 call 0xe4 ; 0xe4 <__udivmodhi4> 第一次除法,16位无符号
a4: 60 93 65 00 sts 0x0065, r22
data = temp/1000; //千位
data = (temp%1000)/100; //百位
a8: c9 01 movw r24, r18
aa: 68 ee ldi r22, 0xE8 ; 232
ac: 73 e0 ldi r23, 0x03 ; 3
ae: 0e 94 72 00 call 0xe4 ; 0xe4 <__udivmodhi4> 第二次除法,16位无符号
b2: 64 e6 ldi r22, 0x64 ; 100
b4: 70 e0 ldi r23, 0x00 ; 0
b6: 0e 94 72 00 call 0xe4 ; 0xe4 <__udivmodhi4> 第三次除法,16位无符号
ba: 60 93 64 00 sts 0x0064, r22
data = (temp%100)/10 ; //十位
be: c9 01 movw r24, r18
c0: 64 e6 ldi r22, 0x64 ; 100
c2: 70 e0 ldi r23, 0x00 ; 0
c4: 0e 94 72 00 call 0xe4 ; 0xe4 <__udivmodhi4> 第四次除法,16位无符号
c8: 6a e0 ldi r22, 0x0A ; 10
ca: 70 e0 ldi r23, 0x00 ; 0
cc: 0e 94 72 00 call 0xe4 ; 0xe4 <__udivmodhi4> 第五次除法,16位无符号
d0: 60 93 63 00 sts 0x0063, r22
data = temp%10 ; //个位
d4: c9 01 movw r24, r18
d6: 6a e0 ldi r22, 0x0A ; 10
d8: 70 e0 ldi r23, 0x00 ; 0
da: 0e 94 72 00 call 0xe4 ; 0xe4 <__udivmodhi4> 第六次除法,16位无符号
de: 80 93 62 00 sts 0x0062, r24
e2: ff cf rjmp .-2 ; 0xe2 <main+0x50>
本贴被 ATmega32 编辑过,最后修改时间:2008-11-21,09:09:37. 上官大侠在C上的造诣真的不一般啊!请推荐几本关于C的好书,谢谢! void HexToBCD(unsigned int t)
{
char i=0;
unsigned int j=0;
for(i=0,j=0;j<t-10000;i++,j+=10000)
{}
buffer=i; //万位
t=t-j;
for(i=0,j=0;j<t-1000;i++,j+=1000)
{}
buffer=i; //千位
t=t-j;
for(i=0,j=0;j<t-100;i++,j+=100)
{}
buffer=i; //百位
t=t-j;
for(i=0,j=0;j<t-10;i++,j+=10)
{}
buffer=i; //十位
buffer=t-j; //个位
}
------------------------------------------------------
BUG 15楼的程序和楼主的其实没多大差别,只是将i改成char型,且还有化简空间,如下可以少一个中间变量j和少几次算.
void HexToBCD(unsigned int t)
{
char i=0;
for(i=0;t>=10000;i++,t-=10000)
{}
buffer=i; //万位
for(i=0;t>=1000;i++,t-=1000)
{}
buffer=i; //千位
for(i=0;t>=-100;i++,t-=100)
{}
buffer=i; //百位
for(i=0;t>=10;i++,t-=10)
{}
buffer=i; //十位
buffer=t; //个位
} 我只是指出它有BUG 15楼的程序和楼主的其实没多大差别,只是将i改成char型,且还有化简空间,如下可以少一个中间变量j和少几次算.
void HexToBCD(unsigned int t)
{
char i=0;
for(i=0;t>=10000;i++,t-=10000)
{}
buffer=i; //万位
for(i=0;t>=1000;i++,t-=1000)
{}
buffer=i; //千位
for(i=0;t>=- 100;i++,t-=100)
{}
buffer=i; //百位
for(i=0;t>=10;i++,t-=10)
{}
buffer=i; //十位
buffer=t; //个位
}
----------------------------------------------------
同样又是BUG
本贴被 foreverwolfer 编辑过,最后修改时间:2008-11-21,10:11:30. 呵呵挺热闹的。也贴一段我写的,比直接用/和%快很多,如有BUG请指正。大家有更好的也贴上来吧:)
// 调用参数 (显示缓冲首地址, 要显示的16位整数); 例: Hex_BCD(DispBuf, temp)
void Hex_BCD(unsigned char *p, unsigned int i)
{
unsigned char n;
for (n=0; i>=10000; ++n)
{
i -= 10000; // 万位
}
*p++ = n;
for (n=0; i>=1000; ++n)
{
i -= 1000;
}
*p++ = n; // 千位
for (n=0; i>=100; ++n)
{
i -= 100;
}
*p++ = n; // 百位
for (n=0; i>=10; ++n)
{
i -= 10;
}
*p++ = n; // 十位
*p++ = i; // 个位
} //使用例子
unsigned char DispBuf;
void main (void)
{
unsigned int temp=0xFFFF;
//。。。。
while (1)
{
//。。。。
Hex_BCD(DispBuf, temp); // 将temp转为十进制显示
}
}
楼上"万位"注释没放正:) 【14楼】 418425051 阿宽
《C primer Plus》和C99标准文档。
我C语言其实也很一般。
本贴被 ATmega32 编辑过,最后修改时间:2008-11-21,17:35:04. 呵呵,更正16楼笔误,t>=-100,多了个负号。Keil仿真,用最大值 65535,函数执行时长为352周期。
3楼方法只一次int除法是142周期,可见16楼的没什么优势
本贴被 cowboy 编辑过,最后修改时间:2008-11-21,12:34:08. 完整的程序如下:
//最大转换数为 65535
unsigned int t;
char buffer={0,0,0,0,0};
void HexToBCD(unsigned int t)
{
char i=0;
unsigned int j=0;
if(t>=10000)
{
for(i=0,j=0;j<=t-10000;i++,j+=10000)
{}
buffer=i; //万位
t=t-j;
}
if(t>=1000)
{
for(i=0,j=0;j<=t-1000;i++,j+=1000)
{}
buffer=i; //千位
t=t-j;
}
if(t>=100)
{
for(i=0,j=0;j<=t-100;i++,j+=100)
{}
buffer=i; //百位
t=t-j;
}
if(t>=10)
{
for(i=0,j=0;j<=t-10;i++,j+=10)
{}
buffer=i; //十位
buffer=t-j; //个位
}
else buffer=t;
}
void main()
{
unsigned int x;
x=65535;
HexToBCD(x);
while(1);
}
运行周期与时间测试:(ICCAVR6.3)---函数运行周期最大为315,时间为40us
http://cache.amobbs.com/bbs_upload782111/files_11/ourdev_506575.jpg
(原文件名:001.jpg)
http://cache.amobbs.com/bbs_upload782111/files_11/ourdev_506576.jpg
(原文件名:002.jpg) 参照 【16楼】 cowboy 的程序,改如下,运行时间为30us,运行周期为240
unsigned t;
char buffer={0,0,0,0,0};
void HexToBCD(unsigned int t)
{
char i;
if(t>=10000)
{
for(i=0;t>=10000;i++,t-=10000)
{}
buffer=i; //万位
}
if(t>=1000)
{
for(i=0;t>=1000;i++,t-=1000)
{}
buffer=i; //千位
}
if(t>=100)
{
for(i=0;t>=100;i++,t-=100)
{}
buffer=i; //百位
}
if(t>=10)
{
for(i=0;t>=10;i++,t-=10)
{}
buffer=i; //十位
buffer=t; //个位
}
else buffer=t; //个位
}
void main()
{
unsigned int x;
x=65535;
HexToBCD(x);
while(1);
} 【24楼】 dd123 ,没必要用那几个if判断语句了,在for里面已隐含了这个判断,你这样改,反而有错,当某位为0时,buffer值不更新.
似乎没必要这样再搞下去,按【3楼】 ATmega32 上官金虹方法最快,即一次16位除法,两次8位除法. 谢谢上官及楼上诸位!学习一下。
看了12/13楼上官的对比,看来差别很大,我把程序改一下看看。
本贴被 999999 编辑过,最后修改时间:2008-11-21,14:48:07. 我没有装GCC软件,所以无法验证 上官金虹 程序的快慢。
上面我重改过的程序确实是画蛇添足,把if语句及最后1个else语句去掉一样运行。
另外,
cowboy 在【22楼】说: 3楼方法只一次int除法是142周期,可见16楼的没什么优势
又在【25楼】说:按【3楼】 ATmega32 上官金虹方法最快,即一次16位除法,两次8位除法。
是不是验证了一下?但据上面 上官司金虹 的测试,有399个时钟周期,不知快在哪里? 楼上,你把函数写个这种格式,
void HexToBCD(unsigned char *p,unsigned int t)
并确保函数没有被优化成内联函数,再来比较。
本贴被 ATmega32 编辑过,最后修改时间:2008-11-21,16:23:15. typedef union {
__u8 byte;
__u16 bank;
}__u16_bank_t;
typedef union {
__u8 byte;
__u32 bank;
}__u32_bank_t;
//16进制到压缩BCD码
__u16 tools_hex_to_bcd(__u8 * buffer, __u16 data_word)
{
__u32_bank_t TempA;
__u32_bank_t TempB;
__u8 i,Temp;
TempA.bank = data_word;
TempB.bank = 0;
for(i = 0;i < 16;i ++) {
TempA.bank <<= 1;
if (TempA.byte & 0x01) {
TempB.bank <<= 1;
TempB.bank |= 0x01;
}
else TempB.bank <<= 1;
Temp = TempB.byte + 0x03;
if (i < 15) {
if (Temp & 0x08) TempB.byte = Temp;
Temp = TempB.byte + 0x30;
if (Temp & 0x80) TempB.byte = Temp;
Temp = TempB.byte + 0x03;
if (Temp & 0x08) TempB.byte = Temp;
Temp = TempB.byte + 0x30;
if (Temp & 0x80) TempB.byte = Temp;
Temp = TempB.byte + 0x03;
if (Temp & 0x08) TempB.byte = Temp;
Temp = TempB.byte + 0x30;
if (Temp & 0x80) TempB.byte = Temp;
}
}
return TempB.bank;
} http://cache.amobbs.com/bbs_upload782111/files_11/ourdev_506894.jpg
(原文件名:QQ截图未命名.jpg)
程序修改前后是有区别,再次谢谢上官
本贴被 999999 编辑过,最后修改时间:2008-11-21,16:30:46. 调试了【21楼】 ATmega32 上官金虹 的程序发现出错
http://cache.amobbs.com/bbs_upload782111/files_11/ourdev_506974.JPG
(原文件名:div.JPG)
buf1是上官金虹的程序结果
buf2是我19楼的程序结果
本贴被 shinehjx 编辑过,最后修改时间:2008-11-21,16:57:48. void HEX_TO_BCD1(unsigned char *p,unsigned int data)
{
udiv16_t divmod16;
udiv8_t divmod8;
divmod16=udiv16(data,100);
divmod8=udiv8(divmod16.quot,10);
p=divmod8.quot;
p=divmod8.rem;
divmod8=udiv8(divmod16.rem,10);
p=divmod8.quot;
p=divmod8.rem;
}
-------------------------------------------------
加入p后比较才公平点 上官金虹 的程序是要求data要少于10000 我仿真的结果显示,并不是ATmega32 上官金虹的方法快,而是减法较快
楼主的程序和我19楼的程序基本一样的(我只是运用了指针),都可能简化成如下
void HexToBCD(unsigned int t)
{
unsigned char i;
for(i=0;t>=10000;i++,t-=10000);
buffer=i; //万位
for(i=0;t>=1000;i++,t-=1000);
buffer=i; //千位
for(i=0;t>=100;i++,t-=100);
buffer=i; //百位
for(i=0;t>=10;i++,t-=10);
buffer=i; //十位
buffer=t; //个位
}
再变为用指针的格式(使用指针自加的方法较为灵活,当显示多个整数或改变显示位置时会更为方便)
void HexToBCD(unsigned char *p,unsigned int t)
{
unsigned char i;
for(i=0;t>=10000;i++,t-=10000);
*p++=i; //万位
for(i=0;t>=1000;i++,t-=1000);
*p++=i; //千位
for(i=0;t>=100;i++,t-=100);
*p++=i; //百位
for(i=0;t>=10;i++,t-=10);
*p++=i; //十位
*p=t; //个位
}
调试图片
http://cache.amobbs.com/bbs_upload782111/files_11/ourdev_507095.JPG
(原文件名:1.JPG)
http://cache.amobbs.com/bbs_upload782111/files_11/ourdev_507096.JPG
(原文件名:2.JPG)
http://cache.amobbs.com/bbs_upload782111/files_11/ourdev_507097.JPG
(原文件名:3.JPG)
http://cache.amobbs.com/bbs_upload782111/files_11/ourdev_507098.JPG
(原文件名:结果.JPG)
工程文件,有兴趣的朋友试下ourdev_507099.rar(文件大小:14K) (原文件名:test.rar)
------修改原因:补上图片及文件
本贴被 shinehjx 编辑过,最后修改时间:2008-11-21,17:45:58. 关注~ 的确是减法快,看来我是井底只蛙了。
对于无符号除法
如果被除数和除数的值相差不大,即除得的商值比较小,那么用减法更快。
如果被除数和除数的值相差很大,即除得的商值比较大,那么用除法更快。
临界值肯能就在10左右。
本贴被 ATmega32 编辑过,最后修改时间:2008-11-22,09:10:48. 发一个,大家看看快步快
//==========一个十六进制的INT型转换为一个压缩的BCD码INT型=========
unsigned int IntHEXToIntBCD( unsigned int temp )
{
unsigned char a,b;
unsigned int c;
a = temp % 10;
a += ( ( ( temp / 10 ) % 10 ) << 4 );
temp = temp / 100;
b = temp % 10;
b += ( ( ( temp / 10 ) % 10 ) << 4 );
c = b;
c = ( c << 8 );
c += a;
return( c );
} 呵呵,又想到一个更快的方法,用类似A/D转换的逐次逼近法,只需最多11次无符号整型减法就完成。
void HexToBCD(unsigned int t)
{
unsigned char i=0;
/*if (t>=40000) {i=4; t-=40000;} //如果t<=9999,则这部分可不用。
if (t>=20000) {i+=2;t-=20000;}
if (t>=10000) {i++; t-=10000;}
buffer=i;
i=0;
*/
if (t>=8000) {i=8; t-=8000;}
if (t>=4000) {i+=4;t-=4000;}
if (t>=2000) {i+=2;t-=2000;}
if (t>=1000) {i++; t-=1000;}
buffer=i;
i=0;
if (t>=800) {i=8; t-=800;}
if (t>=400) {i+=4;t-=400;}
if (t>=200) {i+=2;t-=200;}
if (t>=100) {i++; t-=100;}
buffer=i;
i=0;
if (t>=80) {i=8; t-=80;}
if (t>=40) {i+=4;t-=40;}
if (t>=20) {i+=2;t-=20;}
if (t>=10) {i++; t-=10;}
buffer=i;
buffer=t;
} 学习了。
楼上的程序应该是最快的了。 写个汇编的更快!
呵呵! 38楼 cowboy 的方法有创意,综合也较快,AVR 8M下调试由0~65535连续转换,耗时 1027.26ms
34楼的方法耗时 1628.71ms 那仿真软件用的啥啊? 进一步提速,可在t<200时,用一 char型变量代替t进行运算,估计可再快10%~20%
别,象51有硬件乘除法,则个位和十位数不必用比较求行,用8位除法更快. 【38楼】 cowboy 把减法运算转换发挥到极至了。是呵,当t<200时完全可以改用char类型运算了。 按43楼cowboy的方法提速成功,由原来的 1027.26ms 缩为 969.92ms
// 测试程序
unsigned char buf;
unsigned int temp;
void HexToBCD(unsigned char *p, unsigned int t);
int main()
{
temp = 0;
while(1)
{
HexToBCD(buf,temp);
if (temp == 0xFFFF)
{
while(1);
}
++temp;
}
}
void HexToBCD(unsigned char *p, unsigned int t)
{
unsigned char i=0;
unsigned char s;
if (t>=40000) {i=4; t-=40000;} //如果t<=9999,则这部分可不用。
if (t>=20000) {i+=2;t-=20000;}
if (t>=10000) {i++; t-=10000;}
*p++=i;;
i=0;
if (t>=8000) {i=8; t-=8000;}
if (t>=4000) {i+=4;t-=4000;}
if (t>=2000) {i+=2;t-=2000;}
if (t>=1000) {i++; t-=1000;}
*p++=i;
i=0;
if (t>=800) {i=8; t-=800;}
if (t>=400) {i+=4;t-=400;}
if (t>=200) {i+=2;t-=200;}
s=t;
if (s>=100) {i++; s-=100;}
*p++=i;
i=0;
if (s>=80) {i=8; s-=80;}
if (s>=40) {i+=4;s-=40;}
if (s>=20) {i+=2;s-=20;}
if (s>=10) {i++; s-=10;}
*p++=i;
*p=s;
} 越来越好了,很牛,应该穿条裤子了! 对。。穿裤子了! 平时经常会用到,标记一下。 都说AVR好,连个硬件乘/除法器都没有。
又说51怎么怎么不爽,好歹人家还有个硬件乘/除法器。
直接用/和%运算得了,好移植。 好热闹... HEXTOBCD最快之争吗,呵呵
结果是谁胜出? 好, 记下! 有用,先收藏! mark 55楼xuyiyi 许意义,你对你程序的速度测试过没有?【45楼】 shinehjx 测试结果为:转换一次约15US(8MHZ CLK,122T)我用51的ASM优化上述程序,在51环境中程序运行时间是 77~107机器周期(若用AVR的汇编估计可在80周期以下),我认为是比较快了。我用51的ASM,移位法转换,要用200周期以上,速度跟上面程序根本没法比,我没装AVR的编译,所以试不了,你不妨试试你程序的执行速度!
本贴被 cowboy 编辑过,最后修改时间:2008-11-23,15:41:16. 程序运行的快慢首先取决于计算方法--算法,C语言中好的算法不一定比差的算法汇编慢,有时比汇编还要快。 我也用过类似方法,比除法及求余要快吧. 子程序处理速度快慢,我认为不应以平以平均速度来衡量,应该以任意输入参数中最慢的一次为准,这样才能保证对于任意输入参数,都能在规定的时间内处理完毕,这样才可以估算系统对其它事件的响应速度。以楼上HexToBCD程序为例,当输入参数 t = 7777 时,处理时间最长。优化后的HexToBCD1程序中增加 goto 语句,确实可以使某些特别的输入参数(如9999)处理速度有所提升,但对于输入参数 t = 7777,却没有任何改善,也就是说程序最长的处理时间没有改善,因而这样优化,似乎意义不大。 算法呀。。。。。。。 没得说,楼上的都是好汉,比我想得好太多啦,抄袭~!~ 眼花...
前辈们推动了中国电子发展的新辉煌...
致敬... 学习 Hex to bcd转换的算法比较 作者[晓奇]©
--- 晓奇工作室---
C语言主程序部分
// 左移移位法作hex to bcd转换的算法程序,加上这一段调用演示,以察看运行结果
// 12M晶振时汇编算法运行时间大约为1339ns,C语言减法转换时间大约897ns
// 不同的输入数据在C语言的减法转换时间略有不同,最大时间59999时为1231ns
// 所以C语言的减法运算还是略快一些。
// http://www.xiao-qi.com/晓奇工作室收集整理,Keil C51下调试运行通过
#include <reg51.h>
unsigned char bcdData;
unsigned int ihexs;
unsigned char disp_buffer;
extern void hex_bcd(unsigned int iHex); // 声明外部函数
void hextobcd(unsigned int hexs,unsigned char j);
void main(void){
ihexs = 59999;
hex_bcd(ihexs);
hextobcd(ihexs,5);
while(1);
}
/***************************
hex to bcd 转换程序
***************************/
void hextobcd(unsigned int hexs,unsigned char j) {
unsigned int va;
unsigned char i;
va = 10000; //最大数级万位
for (i=j-1;i;i--) {
disp_buffer = 0; //目标数组清零
while ((hexs>=va)&&(va>9)) {
hexs -= va; //减除数
disp_buffer++; //商位加1
}
va /= 10; //除数除10,指向低一位
}
disp_buffer=hexs; //最后个位数
}
汇编语言模块,建立另一个文件
NAME HEX_BCD
;*************************************************************************
; Hex to 压缩BCD码的转换(每4位代表一位bcd码)
; 输入Hex:R6R7 返回地址:bcdData
;*bcdData = BCD(R6R7)
; http://www.xiao-qi.com/晓奇工作室收集整理,Keil C51下调试运行通过
;*************************************************************************
?PR?_HEX_BCD?HEX_BCD SEGMENT CODE ; 码段定义叙述,需要对本文件内的所有
?PR?_BCD_ADJ?HEX_BCD SEGMENT CODE ; 码段定义叙述,需要对本文件内的所有
PUBLIC _HEX_BCD
EXTRN DATA(bcdData)
RSEG?PR?_HEX_BCD?HEX_BCD
_HEX_BCD:CLR A ;目标数据清零初始化
MOV bcdData, A
MOV bcdData+1, A
MOV bcdData+2, A
MOV R2, #15 ; 共进行15次循环移位运算
H_B0: MOV A, R7 ; 低8位
RLC A ; 大循环左移
MOV R7, A ; 回存
MOV A, R6 ; 高8位
RLC A ; 大循环左移
MOV R6, A ; 回存。至此已获得当前数据的最高位
MOV A, bcdData+2 ; 目标数最低8位
RLC A ; 移入原数据的最高位
ACALL _BCD_ADJ ; 压缩BCD码的十进制调整
MOV bcdData+2, A ; 存回
MOV A, bcdData+1 ; 目标数中间8位
RLC A ; 移入进位位
ACALL _BCD_ADJ ; 压缩BCD码的十进制调整
MOV bcdData+1, A ; 存回
MOV A, bcdData ; 目标数最高8位
RLC A ; 移入进位位
ACALL _BCD_ADJ ; 压缩BCD码的十进制调整
MOV bcdData, A ; 存回
DJNZ R2, H_B0 ; 继续下一轮循环
; 目标数据再左移一位,将原数据的最后一位放入,这样总共进行了16次循环移位
MOV A, R6
RLC A
MOV A, bcdData+2
RLC A
MOV bcdData+2, A
MOV A, bcdData+1
RLC A
MOV bcdData+1, A
MOV A, bcdData
RLC A
MOV bcdData, A
RET
;*************************************************************************
; 压缩BCD码的十进制调整,基本思路:逢十进一,在这里因为还保留着最后一次左
; 移, 所以进位判别的0AH(0A0H)变成相对右移了一位的05H(050H), 而强迫进行进位
; 的方法是加上一个数字3(左移一位后就是6)。程序保护PSW
; 输入返回均使用Acc累加器
;*************************************************************************
RSEG?PR?_BCD_ADJ?HEX_BCD
_BCD_ADJ:
PUSH PSW
PUSH ACC
CJNE A, #50H, $+3 ; 高4位。做一个比较产生状态标志
JC B1 ; 如果小于#50H, 不用进位处理
POP ACC ; 取原数据作修改
ADD A, #30H ; 加上#30h,留待下一轮左移时产生进位
PUSH ACC ; 存回原数据
B1: ANL A, #0FH ; 仅考虑低4位
CJNE A, #5, $+3 ; 做一个比较产生状态标志
JC B2 ; 如果小于#5H, 不用进位处理
POP ACC ; 取原数据作修改
ADD A, #3 ; 加上#3h,留待下一轮左移时产生进位
PUSH ACC ; 存回原数据
B2: POP ACC ; 恢复现场
POP PSW
RET
end
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
©的快速算法,速度更快。
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
?PR?_HEX_BCD?HEX_BCD SEGMENT CODE
PUBLIC _HEX_BCD
RSEG?PR?_HEX_BCD?HEX_BCD
_HEX_BCD:
CLR A ;BCD码初始化
MOV R3,A
MOV R4,A
MOV R5,A
MOV R2,#10H ;转换双字节十六进制整数
HB3:
MOV A,R7 ;从高端移出待转换数的一位到CY中
RLC A
MOV R7,A
MOV A,R6
RLC A
MOV R6,A
MOV A,R3 ;BCD码带进位自身相加,相当于乘2
ADDC A,R3
DA A ;十进制调整
MOV R3,A
MOV A,R4
ADDC A,R4
DA A
MOV R4,A
MOV A,R5
ADDC A,R5
MOV R5,A ;双字节十六进制数的万位数不超过6,不用调整
DJNZ R2,HB3 ;处理完16bit
mov a,r3
mov r7,a
mov a,r4
mov r6,a
mov r4,#0
RET
end
调用方法:
extern unsigned int hex_bcd(unsigned int iHex); /*声明外部函数*/
或
extern unsigned long hex_bcd(unsigned int iHex); /*声明外部函数*/ unsignedint code div={10000,1000,100,10,1};
unsigned charLED;
void HEX_bcd(void){
unsigned char j;
unsigned intk;
k=ex;//外部data.
for(j=0;j<5;j++){
while((int)k>=div){LED+=1;(int)k-=div;};//查表法,不用div=10000; div/=10;
}
}
如何。 【66楼】 yjbin
// 12M晶振时汇编算法运行时间大约为1339ns,C语言减法转换时间大约897ns
笔误吧, ns 应为 us, 用汇编,超过100us 就别讨论了, 早有牛人做到 60 us 以下. 顶 信号找到这帖子! 以前没怎么注意过执行时间这个问题,长见识了 强帖,值得研究 值得收藏,曾经为这个问题困扰过。 学习了。 很好的放算法,加上几个GOTO会更好
void HexToBCD(unsigned char *p, unsigned int t)
{
unsigned char i=0;
unsigned char s;
if (t>=40000) {i =4;t-=40000;} //如果t<=9999,则这部分可不用。
if (t>=20000) {i+=2;t-=20000;}
if (t>=10000) {i++; t-=10000;}
*p++=i;;
i=0;
if (t>=8000) {i =8;t-=8000;goto compare1000;}
if (t>=4000) {i+=4;t-=4000;}
if (t>=2000) {i+=2;t-=2000;}
compare1000:
if (t>=1000) {i++; t-=1000;}
*p++=i;
i=0;
if (t>=800) {i =8;t-=800;goto compare1000;}
if (t>=400) {i+=4;t-=400;}
if (t>=200) {i+=2;t-=200;}
compare100:
s=(unsigned char)t;
if (s>=100) {i++; s-=100;}
*p++=i;
i=0;
if (s>=80) {i =8;s-=80;goto compare10;}
if (s>=40) {i+=4;s-=40;}
if (s>=20) {i+=2;s-=20;}
compare10:
if (s>=10) {i++; s-=10;}
*p++=i;
*p=s;
} 前辈们继续讨论,吾等受益匪浅 MARK H2B 回的第一个贴,留念下 很有效率 记号,备用 用过任意16位二进制转十进制最快的是标准51的用76T,24MHZ时是38us,用C8051等不超过8us。C程序的哦! 真热闹,学习的气氛非常浓。我也有个想法,不知道用“&”运算可不可以优化? mark, 学习了! 先作记号。 51,keil软仿了一下。优化设置默认,12M晶振。X=45536
楼主的程序需要168字节空间。38楼cowboy 的需要436字节空间。
楼主的程序转换时间为480us。38楼cowboy 的转换时间为193us。
大家各取所需吧。 记号 mark 收藏 一味的求快是不行的,说白了是时间和空间的关系~~不同场合要用不同的版本~ mark 马克 哈哈,好,收下学习! mark mark mark Mark! 各位的讨论精彩!很有用,mark! 长见识了向各位高手致敬! 看下 mark 怎么我看到的都是乱码呀,几乎旧贴都是这样,很可惜,
显示的乱码下面这样:
if (Temp & 0x08) TempB.byte = Temp;
Temp = TempB.byte + 0x30;
if (Temp & 0x80) TempB.byte = Temp;
Temp = TempB.byte + 0x03;
if (Temp & 0x08) TempB.byte = Temp;
Temp = TempB.byte + 0x30;
& 谢谢楼主......
页:
[1]