JamesErik 发表于 2013-2-6 13:33:29

悬赏!怎么最快的求数字的各位数字

常用的:

        temp=number%100000000/10000000+48;
        temp=number%10000000/1000000+48;
        temp=number%1000000/100000+48;
        temp=number%100000/10000+48;
        temp=number%10000/1000+48;
        temp=number%1000/100+48;
        temp=number%100/10+48;
        temp=number%10+48;

就是计算量有点大,很耗时,求效率高的算法,谢谢!

yuyu87 发表于 2013-2-6 13:35:30


volatile uint8_t tostr_reg;        //数字转字符

uint8_t * numtostr(uint32_t num,uint32_t len,uint32_t min){        //数字转换为字符,存储在 tostr_reg数字,需要长度(含 小数点),小数位数
        uint32_t i;
        for(i=len;i>0;i--){        //124.300100.0
                if(min&&(min==len-i))
                        tostr_reg='.';
                else{
                        tostr_reg=(num % 10) + 0x30;
                        num/=10;
                }
        }
        tostr_reg=0;
        return (uint8_t *)&tostr_reg;
}

我一个项目里用的,

yuyu87 发表于 2013-2-6 13:37:05

比如 数字 123需要转换成字符 “12.3"
则调用numtostr(123,4,1)
返回的是转换好的字符指针

lugang_2920213 发表于 2013-2-6 13:44:09

不知道楼主要处理的数据是否为浮点数?

hhxb 发表于 2013-2-6 13:45:45

楼主是几位单片机?

.titrwh 发表于 2013-2-6 13:49:05

如果只要时间最快,就用查表,只是要牺牲很大很大的空间。

JamesErik 发表于 2013-2-6 14:17:10

用的AVR,处理的长整形,晶振1M,资源比较紧张啊

JamesErik 发表于 2013-2-6 14:18:21

lugang_2920213 发表于 2013-2-6 13:44 static/image/common/back.gif
不知道楼主要处理的数据是否为浮点数?

长整型,需要知道低八位

JamesErik 发表于 2013-2-6 14:18:51

yuyu87 发表于 2013-2-6 13:37 static/image/common/back.gif
比如 数字 123需要转换成字符 “12.3"
则调用numtostr(123,4,1)
返回的是转换好的字符指针 ...

程序看懂了,看有没有更好的,谢谢!

JamesErik 发表于 2013-2-6 14:19:17

hhxb 发表于 2013-2-6 13:45 static/image/common/back.gif
楼主是几位单片机?

AVR,8位的

JamesErik 发表于 2013-2-6 14:19:51

.titrwh 发表于 2013-2-6 13:49 static/image/common/back.gif
如果只要时间最快,就用查表,只是要牺牲很大很大的空间。

这个查表的话需要的空间太大了

yuyu87 发表于 2013-2-6 14:27:46

JamesErik 发表于 2013-2-6 14:18 static/image/common/back.gif
程序看懂了,看有没有更好的,谢谢!

我感觉还可以,自己写的,你到网上搜下吧,类似的代码蛮多的

yuyu87 发表于 2013-2-6 14:28:55

JamesErik 发表于 2013-2-6 14:17 static/image/common/back.gif
用的AVR,处理的长整形,晶振1M,资源比较紧张啊

1M晶振?也太小了点吧?那你这样计算一下20mS都不止

JamesErik 发表于 2013-2-6 14:32:10

yuyu87 发表于 2013-2-6 14:28 static/image/common/back.gif
1M晶振?也太小了点吧?那你这样计算一下20mS都不止

被光耦整了,只好降低晶振了

canspider 发表于 2013-2-6 14:37:16

再快也得要做8次除法,还是余数和商同时得到的那种
你的数据中间需要做运算吗?如果不需要可以把数据直接存成BCD的形式
如果只做加减,用BCD也还能解决

JamesErik 发表于 2013-2-6 14:39:02

canspider 发表于 2013-2-6 14:37 static/image/common/back.gif
再快也得要做8次除法,还是余数和商同时得到的那种
你的数据中间需要做运算吗?如果不需要可以把数据直接存 ...

一般是求出来给串口或者1602之类的

takashiki 发表于 2013-2-6 14:43:16

AVR,8位的,1M晶振,有整数硬件乘法及定点小数硬件乘法,没有硬件除法。因此,凡是出现了/、%的效率都不可能高,代码也不可能少。必须转换成移位、乘法和减法,极有可能配合查表。

你到底是要效率(时间)还是ROM占用(空间),还是两者兼顾?要效率ROM占用就大,要ROM占用小效率必然比较低,两者兼顾则两者都存在短处,你究竟想如何取舍?

takashiki 发表于 2013-2-6 14:51:48

JamesErik 发表于 2013-2-6 14:18 static/image/common/back.gif
长整型,需要知道低八位

是输入的范围是0~99999999还是在整个long范围内的数值只截取低八位(比如允许100000002,但是只显示2)?两者差异特别大

JamesErik 发表于 2013-2-6 14:59:59

takashiki 发表于 2013-2-6 14:51 static/image/common/back.gif
是输入的范围是0~99999999还是在整个long范围内的数值只截取低八位(比如允许100000002,但是只显示2)? ...

数值只在xxxxxxxx(8)内{:biggrin:}

takashiki 发表于 2013-2-6 15:40:28

本帖最后由 takashiki 于 2013-2-6 15:43 编辑

侧重效率的算法

刚才自己稍微算了一下,32位的乘法以及移位效率也不高,还不如直接辗转相减呢。总的时间占用没有详细计算,只提供方案如下,占用Flash很大的。假设输入的为long Input,输出为char temp。temp总是为0,以便于字符串的计算。char temp;

void LongToStr(long Input){                //针对你这个具体项目进行计算
        char c;                                    //目的是尽量使编译器使用寄存器而不是变量,因为AVR的内存访问效率较低。
        unsigned short wordInput;
        unsigned char bytInput;
       
        c = '0';                                 //计算最高位
        if(Input >= 8000000) c += 8, Input -= 8000000;
        if(Input >= 4000000) c += 4, Input -= 4000000;
        if(Input >= 2000000) c += 2, Input -= 2000000;
        if(Input >= 1000000) c += 1, Input -= 1000000;
        temp = c;
       
        c = '0';                                 //计算第二位
        if(Input >= 800000) c += 8, Input -= 800000;
        if(Input >= 400000) c += 4, Input -= 400000;
        if(Input >= 200000) c += 2, Input -= 200000;
        if(Input >= 100000) c += 1, Input -= 100000;
        temp = c;
       
        c = '0';
        if(Input >= 80000) c += 8, Input -= 80000;
        if(Input >= 40000) c += 4, Input -= 40000;
       
        //到这里为止,Input一定小于40000,因此可以采用short类型了!
        wordInput = (unsigned short)Input;
        if(wordInput >= 20000) c += 2, wordInput -= 20000;
        if(wordInput >= 10000) c += 1, wordInput -= 10000;
        temp = c;       
       
        c = '0';
        if(wordInput >= 8000) c += 8, wordInput -= 8000;
        if(wordInput >= 4000) c += 4, wordInput -= 4000;
        if(wordInput >= 2000) c += 2, wordInput -= 2000;
        if(wordInput >= 1000) c += 1, wordInput -= 1000;
        temp = c;
       
        temp = (wordInput * 205u) >> 11;
        bytInput = wordInput - temp * 10;
       
        temp = (unsigned char)((bytInput * 205u)>>8) >> 3;
        bytInput = wordInput - temp * 10;
       
        temp = (unsigned char)((bytInput * 205u)>>8) >> 3;
        temp = wordInput - temp * 10;       
        temp = 0;
}后面的<1000的计算参见我的一个帖子:AVR中除10的优化算法

hhxb 发表于 2013-2-6 16:03:44

楼上强悍呀

大斧 发表于 2013-2-6 18:27:01

/******long变量转换为字符串*******/
static char *ltoa(unsigned long value)
{
unsigned char *ptr=FRE_BUF2;
unsigned long i=1000000000,d;
do
{
d=value/i;//取最高位
*ptr++ = (char)(d + 0x30);//存放到FRE_BUF2//*ptr先赋值再自加
value -= (d * i);
}
while(i/=10);
return FRE_BUF2;
}
代码不长,除法还在

JamesErik 发表于 2013-2-6 19:08:48

takashiki 发表于 2013-2-6 15:40 static/image/common/back.gif
侧重效率的算法

刚才自己稍微算了一下,32位的乘法以及移位效率也不高,还不如直接辗转相减呢。总的时间占 ...

这个牛逼了,初略看不懂,没有除法了,效率应该好很多,看另外帖子的讨论,这个做了近似处理吗?

takashiki 发表于 2013-2-6 22:06:50

JamesErik 发表于 2013-2-6 19:08 static/image/common/back.gif
这个牛逼了,初略看不懂,没有除法了,效率应该好很多,看另外帖子的讨论,这个做了近似处理吗? ...

是的,做了近似处理。所以只有最后面的三位采用了乘法+移位方式,前面的没有,因为会有误差。<1000的使用乘法+移位方式是没有误差的,我当时每一个数字都对比过的。

其实原理很简单,对于每一位来说,采用8421码分别对比,其实就相当于我们小学时学过的除法的试商,只是全部展开了。
减到40000以下时,使用2个字节表示已经足够了,为了效率的考虑,于是单独拿出来特殊处理。

PS:
进一步优化效率,>8后必定不可能大于1了,因此可以再减少2次判断,优化后的片段如下:c = '0';                                 //计算最高位
if(Input >= 8000000) c += 8, Input -= 8000000;
else {
       if(Input >= 4000000) c += 4, Input -= 4000000;
       if(Input >= 2000000) c += 2, Input -= 2000000;
}
if(Input >= 1000000) c += 1, Input -= 1000000;
temp = c;其他的依此类推。

elchb 发表于 2013-2-6 22:54:38

标记一下,高速ltoa算法。

gshuang1 发表于 2013-2-7 14:57:42

你思路是否有问题,钻牛角尖可不好,仅仅是一个显示在那里瞎折腾,你的项目的数据变化率有多快?是否真的需要那么快才可以满足要求的,1602的显示速率那么低,变化那么快根本看不清有何意义,你计算那么快干嘛。。。脑筋转下弯吧
    实在不行可以用16进制显示,这个超快,乘除法都没用到。

JamesErik 发表于 2013-2-7 15:15:08

takashiki 发表于 2013-2-6 22:06 static/image/common/back.gif
是的,做了近似处理。所以只有最后面的三位采用了乘法+移位方式,前面的没有,因为会有误差。8后必 ...

我不知道怎么算周期,我自己弄了一个这个unsigned char u2s_temp;
unsigned char* ulong_to_str(long int num)
{
    //usart_transmit_string("A");
        u2s_temp='\0';//结束标记
               if(num>89999999) {u2s_temp=9+'0';num=num-90000000;}
    else if(num>79999999) {u2s_temp=8+'0';num=num-80000000;}
    else if(num>69999999) {u2s_temp=7+'0';num=num-70000000;}
    else if(num>59999999) {u2s_temp=6+'0';num=num-60000000;}
    else if(num>49999999) {u2s_temp=5+'0';num=num-50000000;}
    else if(num>39999999) {u2s_temp=4+'0';num=num-40000000;}
    else if(num>29999999) {u2s_temp=3+'0';num=num-30000000;}
    else if(num>19999999) {u2s_temp=2+'0';num=num-20000000;}
    else if(num> 9999999) {u2s_temp=1+'0';num=num-10000000;}
    else u2s_temp=0+'0';
   
           if(num>8999999) {u2s_temp=9+'0';num=num-9000000;}
    else if(num>7999999) {u2s_temp=8+'0';num=num-8000000;}
    else if(num>6999999) {u2s_temp=7+'0';num=num-7000000;}
    else if(num>5999999) {u2s_temp=6+'0';num=num-6000000;}
    else if(num>4999999) {u2s_temp=5+'0';num=num-5000000;}
    else if(num>3999999) {u2s_temp=4+'0';num=num-4000000;}
    else if(num>2999999) {u2s_temp=3+'0';num=num-3000000;}
    else if(num>1999999) {u2s_temp=2+'0';num=num-2000000;}
    else if(num> 999999) {u2s_temp=1+'0';num=num-1000000;}
    else u2s_temp=0+'0';
   
           if(num>899999) {u2s_temp=9+'0';num=num-900000;}
    else if(num>799999) {u2s_temp=8+'0';num=num-800000;}
    else if(num>699999) {u2s_temp=7+'0';num=num-700000;}
    else if(num>599999) {u2s_temp=6+'0';num=num-600000;}
    else if(num>499999) {u2s_temp=5+'0';num=num-500000;}
    else if(num>399999) {u2s_temp=4+'0';num=num-400000;}
    else if(num>299999) {u2s_temp=3+'0';num=num-300000;}
    else if(num>199999) {u2s_temp=2+'0';num=num-200000;}
    else if(num> 99999) {u2s_temp=1+'0';num=num-100000;}
    else u2s_temp=0+'0';
   
           if(num>89999) {u2s_temp=9+'0';num=num-90000;}
    else if(num>79999) {u2s_temp=8+'0';num=num-80000;}
    else if(num>69999) {u2s_temp=7+'0';num=num-70000;}
    else if(num>59999) {u2s_temp=6+'0';num=num-60000;}
    else if(num>49999) {u2s_temp=5+'0';num=num-50000;}
    else if(num>39999) {u2s_temp=4+'0';num=num-40000;}
    else if(num>29999) {u2s_temp=3+'0';num=num-30000;}
    else if(num>19999) {u2s_temp=2+'0';num=num-20000;}
    else if(num> 9999) {u2s_temp=1+'0';num=num-10000;}
    else u2s_temp=0+'0';
   
           if(num>8999) {u2s_temp=9+'0';num=num-9000;}
    else if(num>7999) {u2s_temp=8+'0';num=num-8000;}
    else if(num>6999) {u2s_temp=7+'0';num=num-7000;}
    else if(num>5999) {u2s_temp=6+'0';num=num-6000;}
    else if(num>4999) {u2s_temp=5+'0';num=num-5000;}
    else if(num>3999) {u2s_temp=4+'0';num=num-4000;}
    else if(num>2999) {u2s_temp=3+'0';num=num-3000;}
    else if(num>1999) {u2s_temp=2+'0';num=num-2000;}
    else if(num> 999) {u2s_temp=1+'0';num=num-1000;}
    else u2s_temp=0+'0';
   
           if(num>899) {u2s_temp=9+'0';num=num-900;}
    else if(num>799) {u2s_temp=8+'0';num=num-800;}
    else if(num>699) {u2s_temp=7+'0';num=num-700;}
    else if(num>599) {u2s_temp=6+'0';num=num-600;}
    else if(num>499) {u2s_temp=5+'0';num=num-500;}
    else if(num>399) {u2s_temp=4+'0';num=num-400;}
    else if(num>299) {u2s_temp=3+'0';num=num-300;}
    else if(num>199) {u2s_temp=2+'0';num=num-200;}
    else if(num> 99) {u2s_temp=1+'0';num=num-100;}
    else u2s_temp=0+'0';
   
           if(num>89) {u2s_temp=9+'0';num=num-90;}
    else if(num>79) {u2s_temp=8+'0';num=num-80;}
    else if(num>69) {u2s_temp=7+'0';num=num-70;}
    else if(num>59) {u2s_temp=6+'0';num=num-60;}
    else if(num>49) {u2s_temp=5+'0';num=num-50;}
    else if(num>39) {u2s_temp=4+'0';num=num-40;}
    else if(num>29) {u2s_temp=3+'0';num=num-30;}
    else if(num>19) {u2s_temp=2+'0';num=num-20;}
    else if(num> 9) {u2s_temp=1+'0';num=num-10;}
    else u2s_temp=0+'0';
   
    u2s_temp=num+'0';
   
    return u2s_temp;
}原理应该没有问题,还没有实验

没有乘除法了,就是代码量很大

bg6agf 发表于 2013-2-7 22:36:55

这个好久以前都讨论过:怎么还在讨论?要学会搜索 !

bigallium 发表于 2013-2-8 01:54:40

sprintf王道...
页: [1]
查看完整版本: 悬赏!怎么最快的求数字的各位数字