dd123 发表于 2008-11-20 16:51:06

一个速度较快的 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.

cgbabc 发表于 2008-11-20 17:16:13

有创意,顶一个

lzf713 发表于 2008-11-20 17:20:17

不是我打击楼主,其实你用了unsigned int  j,在获取万千百十个位时候,有可能进行多次加法,并且还要使用unsigned int 数据进行判断,速度也不是快很多,不信可以调试看看。如果是unsigned char类型数据则令当别论。

ATmega32 发表于 2008-11-20 17:40:18

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.

shark 发表于 2008-11-20 17:49:36

用减法代替除法。

cowboy 发表于 2008-11-20 17:52:32

还是查表最快

12F675 发表于 2008-11-20 22:53:40

mark

avenbbs 发表于 2008-11-20 23:10:32

真的需要快,就查表,如果没有那种要求,就算吧,别计较太多了

dd123 发表于 2008-11-21 06:42:54

回【2楼】 lzf713 

请教用unsigned char 实现无符号整型的转换方法。



回【3楼】 ATmega32 上官金虹

在8位机里面,多次用除法和乘法对转换速度的影响是不是很大呢?我的原则是,能不用乘除法时则尽量不用乘除法。



回【7楼】 avenbbs 

如果用查表的方法,对于65535这样的数,需要建一个多大的表?



真诚请教与探讨。

999999 发表于 2008-11-21 07:17:37

【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 ;             //个位                



用你的方法,应该可以更简化吧。

xiaobendan 发表于 2008-11-21 07:58:03

我也象楼上这样用的

ATmega32 发表于 2008-11-21 08:57:57

#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>

ATmega32 发表于 2008-11-21 09:08:35

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.

418425051 发表于 2008-11-21 09:15:26

上官大侠在C上的造诣真的不一般啊!请推荐几本关于C的好书,谢谢!

foreverwolfer 发表于 2008-11-21 09:20:54

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

cowboy 发表于 2008-11-21 09:55:36

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;    //个位 

} 

foreverwolfer 发表于 2008-11-21 10:00:13

我只是指出它有BUG

foreverwolfer 发表于 2008-11-21 10:01:41

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.

shinehjx 发表于 2008-11-21 10:37:48

呵呵挺热闹的。也贴一段我写的,比直接用/和%快很多,如有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;                      // 个位

}

shinehjx 发表于 2008-11-21 10:52:07

//使用例子



unsigned char DispBuf;



void main (void)

{

    unsigned int temp=0xFFFF;

    //。。。。

    while (1)

    {

        //。。。。

        Hex_BCD(DispBuf, temp);   // 将temp转为十进制显示

    }

}



楼上"万位"注释没放正:)

ATmega32 发表于 2008-11-21 11:26:18

【14楼】 418425051 阿宽

《C primer Plus》和C99标准文档。



我C语言其实也很一般。



本贴被 ATmega32 编辑过,最后修改时间:2008-11-21,17:35:04.

cowboy 发表于 2008-11-21 12:29:55

呵呵,更正16楼笔误,t>=-100,多了个负号。Keil仿真,用最大值 65535,函数执行时长为352周期。



3楼方法只一次int除法是142周期,可见16楼的没什么优势

本贴被 cowboy 编辑过,最后修改时间:2008-11-21,12:34:08.

dd123 发表于 2008-11-21 12:58:12

完整的程序如下:

//最大转换数为 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) 

dd123 发表于 2008-11-21 14:21:57

参照 【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);

}

cowboy 发表于 2008-11-21 14:39:24

【24楼】 dd123 ,没必要用那几个if判断语句了,在for里面已隐含了这个判断,你这样改,反而有错,当某位为0时,buffer值不更新.

似乎没必要这样再搞下去,按【3楼】 ATmega32 上官金虹方法最快,即一次16位除法,两次8位除法.

999999 发表于 2008-11-21 14:45:06

谢谢上官及楼上诸位!学习一下。
看了12/13楼上官的对比,看来差别很大,我把程序改一下看看。

本贴被 999999 编辑过,最后修改时间:2008-11-21,14:48:07.

dd123 发表于 2008-11-21 16:00:49

我没有装GCC软件,所以无法验证 上官金虹 程序的快慢。

上面我重改过的程序确实是画蛇添足,把if语句及最后1个else语句去掉一样运行。

另外,
 cowboy 在【22楼】说:  3楼方法只一次int除法是142周期,可见16楼的没什么优势
又在【25楼】说:按【3楼】 ATmega32 上官金虹方法最快,即一次16位除法,两次8位除法。

是不是验证了一下?但据上面 上官司金虹 的测试,有399个时钟周期,不知快在哪里?

ATmega32 发表于 2008-11-21 16:01:27

楼上,你把函数写个这种格式,
void HexToBCD(unsigned char *p,unsigned int t) 
并确保函数没有被优化成内联函数,再来比较。

本贴被 ATmega32 编辑过,最后修改时间:2008-11-21,16:23:15.

rayfox 发表于 2008-11-21 16:13:04

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;
}

999999 发表于 2008-11-21 16:29:05

http://cache.amobbs.com/bbs_upload782111/files_11/ourdev_506894.jpg
 (原文件名:QQ截图未命名.jpg) 

程序修改前后是有区别,再次谢谢上官


本贴被 999999 编辑过,最后修改时间:2008-11-21,16:30:46.

shinehjx 发表于 2008-11-21 16:53:59

调试了【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.

foreverwolfer 发表于 2008-11-21 16:54:23

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后比较才公平点

cowboy 发表于 2008-11-21 17:15:46

上官金虹 的程序是要求data要少于10000

shinehjx 发表于 2008-11-21 17:38:32

我仿真的结果显示,并不是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.

bbandpp 发表于 2008-11-21 18:40:23

关注~

ATmega32 发表于 2008-11-21 20:22:40

的确是减法快,看来我是井底只蛙了。


对于无符号除法

如果被除数和除数的值相差不大,即除得的商值比较小,那么用减法更快。
如果被除数和除数的值相差很大,即除得的商值比较大,那么用除法更快。

临界值肯能就在10左右。

本贴被 ATmega32 编辑过,最后修改时间:2008-11-22,09:10:48.

zhangxy 发表于 2008-11-21 20:40:01

发一个,大家看看快步快
//==========一个十六进制的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 ); 
}

cowboy 发表于 2008-11-21 23:13:54

呵呵,又想到一个更快的方法,用类似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;
}

ATmega32 发表于 2008-11-22 09:16:50

学习了。

楼上的程序应该是最快的了。

dengyijun103 发表于 2008-11-22 09:30:04

写个汇编的更快!
呵呵!

shinehjx 发表于 2008-11-22 10:04:29

38楼 cowboy 的方法有创意,综合也较快,AVR 8M下调试由0~65535连续转换,耗时 1027.26ms

34楼的方法耗时 1628.71ms

xiaobendan 发表于 2008-11-22 10:17:57

那仿真软件用的啥啊?

cowboy 发表于 2008-11-22 10:47:35

进一步提速,可在t<200时,用一 char型变量代替t进行运算,估计可再快10%~20%
别,象51有硬件乘除法,则个位和十位数不必用比较求行,用8位除法更快.

dd123 发表于 2008-11-22 10:54:32

【38楼】 cowboy 把减法运算转换发挥到极至了。是呵,当t<200时完全可以改用char类型运算了。

shinehjx 发表于 2008-11-22 11:51:29

按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; 
} 

shark 发表于 2008-11-22 12:35:32

越来越好了,很牛,应该穿条裤子了!

cjr82123 发表于 2008-11-22 18:23:26

对。。穿裤子了!

gchqqi 发表于 2008-11-23 00:31:24

平时经常会用到,标记一下。

gliet_su 发表于 2008-11-23 01:23:11

都说AVR好,连个硬件乘/除法器都没有。
又说51怎么怎么不爽,好歹人家还有个硬件乘/除法器。
直接用/和%运算得了,好移植。

thoro_avr 发表于 2008-11-23 05:03:31

好热闹...

camtime 发表于 2008-11-23 10:04:03

HEXTOBCD最快之争吗,呵呵
结果是谁胜出?

nicksean 发表于 2008-11-23 10:09:30

好, 记下!

lljyes 发表于 2008-11-23 10:59:29

有用,先收藏!

hhrfjz 发表于 2008-11-23 13:47:50

mark

cowboy 发表于 2008-11-23 15:33:11

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.

dd123 发表于 2008-11-23 16:17:17

程序运行的快慢首先取决于计算方法--算法,C语言中好的算法不一定比差的算法汇编慢,有时比汇编还要快。

johnwjl 发表于 2008-11-23 20:49:12

我也用过类似方法,比除法及求余要快吧.

cowboy 发表于 2008-12-6 12:34:01

子程序处理速度快慢,我认为不应以平以平均速度来衡量,应该以任意输入参数中最慢的一次为准,这样才能保证对于任意输入参数,都能在规定的时间内处理完毕,这样才可以估算系统对其它事件的响应速度。以楼上HexToBCD程序为例,当输入参数 t = 7777 时,处理时间最长。优化后的HexToBCD1程序中增加 goto 语句,确实可以使某些特别的输入参数(如9999)处理速度有所提升,但对于输入参数 t = 7777,却没有任何改善,也就是说程序最长的处理时间没有改善,因而这样优化,似乎意义不大。 

pcwinner 发表于 2008-12-6 20:28:24

算法呀。。。。。。。

MANANDFEMAN 发表于 2009-1-16 14:10:47

没得说,楼上的都是好汉,比我想得好太多啦,抄袭~!~

beliwen 发表于 2009-1-17 11:34:16

眼花...

前辈们推动了中国电子发展的新辉煌...

致敬... 学习 

yjbin 发表于 2009-3-5 21:52:55

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
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
&copy;的快速算法,速度更快。
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
?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);    /*声明外部函数*/

ml07077 发表于 2009-3-5 22:10:15

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;

}
}
如何。

cowboy 发表于 2009-3-5 22:36:49

【66楼】 yjbin
// 12M晶振时汇编算法运行时间大约为1339ns,C语言减法转换时间大约897ns

笔误吧, ns 应为 us, 用汇编,超过100us 就别讨论了, 早有牛人做到 60 us 以下.

yunlong 发表于 2009-10-29 23:14:25

tclandmei 发表于 2009-12-15 17:36:14

信号找到这帖子!

ringan865 发表于 2009-12-22 21:22:15

以前没怎么注意过执行时间这个问题,长见识了

fang45 发表于 2009-12-22 21:59:25

强帖,值得研究

xrwf_2009 发表于 2009-12-22 23:11:08

值得收藏,曾经为这个问题困扰过。

hetao7241 发表于 2009-12-22 23:13:25

学习了。

jiangxingyuan 发表于 2009-12-24 09:28:12

很好的放算法,加上几个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;   
}

howmoney 发表于 2009-12-24 10:38:05

前辈们继续讨论,吾等受益匪浅

eduhf_123 发表于 2009-12-24 11:08:00

MARK H2B

sup999 发表于 2010-1-5 18:36:16

回的第一个贴,留念下

elchb 发表于 2010-1-13 17:05:08

很有效率

xwman 发表于 2010-1-13 23:39:31

记号,备用

coody 发表于 2010-1-14 00:06:21

用过任意16位二进制转十进制最快的是标准51的用76T,24MHZ时是38us,用C8051等不超过8us。C程序的哦!

komahe 发表于 2010-1-14 00:43:45

真热闹,学习的气氛非常浓。我也有个想法,不知道用“&”运算可不可以优化?

wormchen 发表于 2010-1-14 09:56:42

mark, 学习了!

phone 发表于 2010-1-14 11:46:02

先作记号。

flyunlimit 发表于 2010-1-14 12:12:23

51,keil软仿了一下。优化设置默认,12M晶振。X=45536

楼主的程序需要168字节空间。38楼cowboy 的需要436字节空间。

楼主的程序转换时间为480us。38楼cowboy 的转换时间为193us。

大家各取所需吧。

ju748 发表于 2010-1-14 13:01:06

记号

beixue 发表于 2010-1-14 14:33:45

mark

fshunj 发表于 2010-3-2 14:48:47

收藏

xczxwy 发表于 2010-3-2 15:30:34

一味的求快是不行的,说白了是时间和空间的关系~~不同场合要用不同的版本~

rainbowu 发表于 2010-10-23 10:27:27

mark

5irmb 发表于 2010-10-23 12:20:15

马克

worldly_guest 发表于 2010-10-24 16:04:47

哈哈,好,收下学习!

zywh 发表于 2011-1-26 15:32:19

mark

zhangxun0712 发表于 2011-2-24 17:37:47

mark

renkaikaiser 发表于 2011-2-24 20:53:30

mark

zhenke 发表于 2011-2-27 13:05:56

Mark!

26532401 发表于 2011-7-9 00:20:04

各位的讨论精彩!很有用,mark!

CHENXIAOTIAN 发表于 2011-7-9 15:42:51

长见识了向各位高手致敬!

mplk 发表于 2011-7-9 16:16:38

看下

yuzr 发表于 2012-2-1 14:22:55

mark

GZLJZ 发表于 2013-1-16 03:17:26

怎么我看到的都是乱码呀,几乎旧贴都是这样,很可惜,
显示的乱码下面这样:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(Temp&nbsp;&&nbsp;0x08)&nbsp;TempB.byte&nbsp;=&nbsp;Temp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Temp&nbsp;=&nbsp;TempB.byte&nbsp;+&nbsp;0x30;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(Temp&nbsp;&&nbsp;0x80)&nbsp;TempB.byte&nbsp;=&nbsp;Temp;

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Temp&nbsp;=&nbsp;TempB.byte&nbsp;+&nbsp;0x03;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(Temp&nbsp;&&nbsp;0x08)&nbsp;TempB.byte&nbsp;=&nbsp;Temp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Temp&nbsp;=&nbsp;TempB.byte&nbsp;+&nbsp;0x30;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&

jz701209李 发表于 2013-4-11 17:01:28

谢谢楼主......
页: [1]
查看完整版本: 一个速度较快的 int 数转换为BCD的程序【恢复】