shafei 发表于 2012-3-29 19:35:41

单片机y=0.09*x-45的数据处理

本帖最后由 shafei 于 2012-3-29 19:50 编辑

我用单片机处理数据遇到一个不大不小的问题,
有一个公式y=0.09*x-45 其中x是unsigned char型,x的取值范围为我有几个问题
一:这里的y定义成什么类型比较好,float型吗?
二:算出y后,我希望对它四舍五入后,返回一个unsigned char型的数据
我的意思是说:算出y后,四舍五入以后,再让y变成unsigned char型
我举一个例子,假如我算出来y为178.3,我想让它四舍五入后变成178,然后返回unsigned char型的178,如果我算出来的是178.8,我想让他四舍五入后变成179,然后返回unsigned char型的179
大家可以帮帮我吗?我用在舵机角度控制上面
刚才有人提醒因为X是从500到2500,所以X不是uchar,应该定义成uint

y574924080 发表于 2012-3-29 19:48:08

这个应该对你有帮助

johnsonz999 发表于 2012-3-29 19:50:48

char的是8位的怎么能在500-2500呢?

jimmy_xt 发表于 2012-3-29 19:55:46

不要定义为浮点数,运算太慢了……
把值扩大100倍
计算y=9*x-4500
按你给出的X值,不会超出int的范围。

shafei 发表于 2012-3-29 20:09:23

本帖最后由 shafei 于 2012-3-30 09:11 编辑

jimmy_xt 发表于 2012-3-29 19:55 static/image/common/back.gif
不要定义为浮点数,运算太慢了……
把值扩大100倍
计算y=9*x-4500


谢谢你了,你给了我一个很好的启发,这样数字就成了0到18000,我可以从判断第2位就可以了,如果第2位大于或者等于5,我就让它第三位加一,如果第2位小于5,后面的两位直接都不要

gaolf_2012 发表于 2012-3-29 20:11:01

近似的做法;两边都乘以256
y=0.09*x-45
得到256*y=23.04*x-11520
如果对数据要求不是很严格的话,可以近似的认为 y = (23*x-11520)>>8
可见当x =500时,23*x=11500,买嘎!nngb的,第一个数就这么不给面子!还是从501开始吧。

lixupeng 发表于 2012-3-29 20:17:25

jimmy_xt 发表于 2012-3-29 19:55 static/image/common/back.gif
不要定义为浮点数,运算太慢了……
把值扩大100倍
计算y=9*x-4500


使用过了这个方法不错^_^

summarize 发表于 2012-3-29 20:22:22

不要定义为浮点数,运算太慢了……
把值扩大100倍
计算y=9*x-4500
按你给出的X值,不会超出int的范围。
-------------------------
思路如4楼所说,要实现四舍五入,如结果用z表示,则还要:
z = y /100;   //之前放大100倍计算,这里除以100则还原为真实值。
y = y /10;    //除10之后比真实值只放大了10倍
y = y % 10;// 对10取余,用于四舍五入。
if(y >= 5)
{
    z += 1;//五入
}
/*else
{
    ;//四舍
}*/

jackielau 发表于 2012-3-29 20:23:26

jimmy_xt 发表于 2012-3-29 19:55 static/image/common/back.gif
不要定义为浮点数,运算太慢了……
把值扩大100倍
计算y=9*x-4500


俺就是这样处理的,精度速度都好

eduhf_123 发表于 2012-3-29 20:25:31

首先unsigned char型变量的取值范围只有,无法存储的数据,所以x的类型应该是unsigned short int。

然后:
y。= 0.09*x - 45
  = 0.09*(x-500)
  = /100

y = INT( /100 + 0.5 )
= INT( /100 + 50/100 )
= INT( /100 )
= INT( [(9*x-4450)]/100 )
= (9*x-4450) div 100
其中,INT(x)表示对x取整,div表示取整除法(即C语言中的“/”运算符)。

在保证x∈的条件下,上述方法的计算结果可以保证在区间,变量y用unsigned char类型即可:
y = (x*9-4450)/100;
等号右边的表达式自动提升到int型进行求值,在赋值的时候会自动截断成unsigned char。

上述算法的最大误差为±0.5,需要int型的乘法、减法、除法运算各一次;如果对误差要求不高,则可以用下式来近似计算:
y =(x-495)/11;
误差范围为,节省一次int类型的乘法运算。

eduhf_123 发表于 2012-3-29 21:13:45

RE: 单片机y=0.09*x-45的数据处理

gaolf_2012 发表于 2012-3-29 20:11 static/image/common/back.gif
近似的做法;两边都乘以256
y=0.09*x-45
得到256*y=23.04*x-11520


改良6楼的办法(加入对四舍五入的处理):
y = x*0.09-45
256*y = x*23.04-11520
y ≈ (x*23-11520)/256
= INT( (x*23-11520)/256 + 0.5 )
= INT( (x*23-11392)/256 )

误差范围为[-1, 0]

shafei 发表于 2012-3-29 21:33:39

本帖最后由 shafei 于 2012-3-29 21:34 编辑

eduhf_123 发表于 2012-3-29 20:25 static/image/common/back.gif
首先unsigned char型变量的取值范围只有,无法存储的数据,所以x的类型应该是unsigned...

谢谢您,也谢谢各位热心的回答,您的做法很详细而且在数据处理方面给了我很好的启发,
我还有一个问题,你这样的话,是不是说,y事先要定义成unsigned char型,?

eduhf_123 发表于 2012-3-29 21:45:58

shafei 发表于 2012-3-29 21:33 static/image/common/back.gif
谢谢您,也谢谢各位热心的回答,您的做法很详细而且在数据处理方面给了我很好的启发,
我还有一个问题, ...

是的,定义成unsigned char类型即可。

xizi 发表于 2012-3-30 04:29:34

支持11楼的简化算法。

Gilgamesh 发表于 2012-3-30 08:34:47

y574924080 发表于 2012-3-29 19:48 static/image/common/back.gif
这个应该对你有帮助

有帮助~

shafei 发表于 2012-3-30 09:34:36

eduhf_123 发表于 2012-3-29 20:25 static/image/common/back.gif
首先unsigned char型变量的取值范围只有,无法存储的数据,所以x的类型应该是unsigned...

等号右边的表达式自动提升到int型进行求值,在赋值的时候会自动截断成unsigned char。
晚辈不理解
y 定义成unsigned char,x定义成unsigned short int
我查看了C语言的书籍,上面写着表达式中出现unsigned short ,short, unsigned char, char型,立即转化为int型,然后用int型参与运算
你说的爬升,就是这个意思,对吗?
但是我又发现了一句话:当双目运算符两边表达式的数据类型不一致时,系统首先把其中较低级类型的操作数转换为与另一个较高类型的操作数相同的数据类型,然后进行运算,计算结果为类型较高的操作数的数据类型
这个地方怎么和公式y=0.09*x-45联系起来思考?
婉谢

y574924080 发表于 2012-3-30 13:15:42

本帖最后由 y574924080 于 2012-3-30 13:16 编辑

eduhf_123 发表于 2012-3-29 21:13 static/image/common/back.gif
改良6楼的办法(加入对四舍五入的处理):
y = x*0.09-45
256*y = x*23.04-11520


您好,您在这三个数据处理的式子里 y = INT( [(9*x-4450)]/100 )   误差范围为[-0.5,0.5]
                                                 y = INT (x-495)/11                   误差范围为                              
                                                 y = INT( (x*23-11392)/256 )   误差范围为[-1, 0]
都给出了误差范围, 我想问您是怎样计算误差范围的呢????

                                          

eduhf_123 发表于 2012-3-30 14:41:35

shafei 发表于 2012-3-30 09:34 static/image/common/back.gif
等号右边的表达式自动提升到int型进行求值,在赋值的时候会自动截断成unsigned char。
晚辈不理解
y 定义 ...

y 定义成unsigned char,x定义成unsigned short int
我查看了C语言的书籍,上面写着表达式中出现unsigned short ,short, unsigned char, char型,立即转化为int型,然后用int型参与运算
你说的爬升,就是这个意思,对吗?

没错,是这样的。
------------------------------------------------------------
但是我又发现了一句话:当双目运算符两边表达式的数据类型不一致时,系统首先把其中较低级类型的操作数转换为与另一个较高类型的操作数相同的数据类型,然后进行运算,计算结果为类型较高的操作数的数据类型

这里说的也是整型提升的问题。
------------------------------------------------------------
这个地方怎么和公式y=0.09*x-45联系起来思考?

如果你直接用这个原始公式来进行计算,由于0.09是浮点型常量,运算时当成double来处理,是最“高级”的类型,所以进行乘法运算时要把变量x也提升成double类型,然后乘法的运算结果是double的;再与45做减法,依然要提升成double型——需要两次浮点运算。
在没有FPU(硬件浮点处理单元)的情况下进行浮点运算,其效率是非常低的,而一般的单片机都是没有FPU的,所以通常我们都尽量避免用单片机进行浮点数处理,而是转换为定点运算来进行处理。
我在10楼提出的办法就是用先乘以100、在除以100的办法来规避浮点运算的,即用9/100来替代0.09;6楼gaolf_2012所采用的是先乘以256、再除以256的办法,这个办法的优点在于最后除以256的除法运算可以直接用移位(在这里其实是直接取结果的高8位)来代替,节省了一次int型的除法运算,但其缺点是,0.09=23.04/256,用23/256代替0.09,引入了约2‰的误差。

chenguanghua 发表于 2012-3-30 15:08:57

4 6 8 10 楼
都给出了算法
之前用的是 6楼的这种算法
同问
y = INT( [(9*x-4450)]/100 )      误差范围为[-0.5,0.5]
y = INT (x-495)/11                   误差范围为                              
y = INT( (x*23-11392)/256 )   误差范围为[-1, 0]
都给出了误差范围, 怎样计算误差范围的呢?

eduhf_123 发表于 2012-3-30 15:20:31

本帖最后由 eduhf_123 于 2012-3-30 15:21 编辑

y574924080 发表于 2012-3-30 13:15 static/image/common/back.gif
您好,您在这三个数据处理的式子里 y = INT( [(9*x-4450)]/100 )   误差范围为[-0.5,0.5]
                                                 y = INT (x-495)/11                   误差范围为                              
                                                 y = INT( (x*23-11392)/256 )   误差范围为[-1, 0]
都给出了误差范围, 我想问您是怎样计算误差范围的呢????

要善用Excel、善用Excel的公式。

eduhf_123 发表于 2012-3-30 15:23:00

chenguanghua 发表于 2012-3-30 15:08 static/image/common/back.gif
4 6 8 10 楼
都给出了算法
之前用的是 6楼的这种算法


见我在20楼的回复。

jssd 发表于 2012-3-30 15:26:01

eduhf_123 发表于 2012-3-30 15:20 static/image/common/back.gif
要善用Excel、善用Excel的公式。

高人啊。我一直以为Excel是文员的工具。。。。汗一个{:sweat:}

eduhf_123 发表于 2012-3-30 15:33:06

summarize 发表于 2012-3-29 20:22 static/image/common/back.gif
不要定义为浮点数,运算太慢了……
把值扩大100倍
计算y=9*x-4500


值得一提的是,8楼这种处理四舍五入的办法效率太低了。
当我们需要对一个除法运算的商进行四舍五入时,只需要在计算除法之前预先给被除数加上除数的一半(即给商预先加上0.5)再做除法就好。

szzyq 发表于 2012-3-30 16:48:26

这招不错

mcuz195 发表于 2012-3-30 17:34:31

误差分析属数理统计一类的学科,没有计算机辅助很废脑筋的,借助EXCEL的确省时省力!

y574924080 发表于 2012-3-30 18:31:00

eduhf_123 发表于 2012-3-30 15:23 static/image/common/back.gif
见我在20楼的回复。

谢谢您了

我去复习Excel了

惭愧,这些都学过,

不会学以致用~~

{:loveliness:}

shafei 发表于 2012-3-30 18:33:24

eduhf_123 发表于 2012-3-30 15:56 static/image/common/back.gif
值得一提的是,8楼这种处理四舍五入的办法效率太低了。
当我们需要对一个除法运算的商进行四舍五入时,只 ...

您这句话对我处理四舍五入提供了一个好方法,学习了!
晚辈看了一半帖子的时候,写的处理方法是这样的:
void display(uint x)
{
        uint y,tmp_0,tmp_1,tmp_2,tmp_3;
        y=9*x-4500;
        tmp_0=y/10000;
        tmp_1=(y%10000)/1000;
        tmp_2=((y%10000)%1000)/100;
        tmp_3=((y%10000)%1000)%100;
        if(tmp_3>=5)
                tmp_2+=1;//以上是我本来的数据处理程序
        P2=0x07;
        P0=table;
        Delay5MS(2);
        P2=0x06;
        P0=table;
        Delay5MS(2);
        P2=0x05;
        P0=table;
        Delay5MS(2);
        P2=0x03;
        P0=table;
        Delay5MS(2);
        P2=0x01;
        P0=table;
        Delay5MS(2);

}
现在尝试改成您说的那样:
void display(unsigned short int x)
{
unsigned char y;
y= (9*x-4450) /100//我这个地方没有错误了吧?
tmp_0=y/100;
tmp_1=(y%100)/10;
tmp_2=(y%100)%10;//后面省略了
}
这个程序里面只是把误差控制在您说的,没有四舍五入。
如果按您对六楼的优化:y= INT( (x*23-11392)/256 )
这里面就加入了四舍五入了对吧,误差控制在[-1,0]之间了,同时x最好从501算起


lyjian 发表于 2012-3-30 18:35:11

学习了,以前都不会用Excel啊

zhiwei 发表于 2012-3-30 18:47:38

精度要求高的话把0.09转换成16bit数据 y=( (U16)(0.09*65536)*x >> 16 )- 45;

shafei 发表于 2012-3-30 19:00:00

zhiwei 发表于 2012-3-30 18:47 static/image/common/back.gif
精度要求高的话把0.09转换成16bit数据 y=( (U16)(0.09*65536)*x >> 16 )- 45;

晚辈不才,
第一个问题:您公式里面的(U16)是什么意思啊?(本人是C语言菜鸟级)直接像您说的这样直接把这个公式敲进去吗?
第二个问题:您这个公式里面的Y定义成什么数据类型?
第三个问题:您给出的这个公式,单片机运行起来,速度怎么样啊?
请高手指点!

vermon 发表于 2012-3-30 19:45:17

移位是一个好方法,算出来的结果在移位,这种应该不受编译器限制

skynet 发表于 2012-3-30 19:51:44

18B20的代码中就有类似处理

shafei 发表于 2012-3-30 21:16:26

skynet 发表于 2012-3-30 19:51 static/image/common/back.gif
18B20的代码中就有类似处理

对,我看过
而且18B20里面是用了查表的方法来确定小数的

zhiwei 发表于 2012-3-30 21:39:03

shafei 发表于 2012-3-30 19:00 static/image/common/back.gif
晚辈不才,
第一个问题:您公式里面的(U16)是什么意思啊?(本人是C语言菜鸟级)直接像您说的这样直接把 ...

写法有点问题,可能得不到正确的结果,应改为:y=((U32)(0.09 * 65536) * x >> 16) - 45;
U16 我一般用type定义的类型,16bit无符号整形数据。根据编译器一般是unsigned short int 吧, 32bit就是unsigned long int。两个16bit数据乘法如果你直接用我那个语句的话可能得不到想要的结果(C编译器对16*16都是得到16位数据,会溢出);如果你用32位机有32位乘法指令的话,这样写就ok,效率绝对高;8位机的话可以写个16*16位出32位,然后取高16bit的子程序(有8*8硬件乘法器的话,4次乘法,两次加法就可以搞定,然后返回高2byte,我用avr时一般都这么做的)。

lsy5110 发表于 2012-3-30 21:41:13

又学一招!

role_2099 发表于 2012-3-30 21:59:55

eduhf_123 发表于 2012-3-29 21:13 static/image/common/back.gif
改良6楼的办法(加入对四舍五入的处理):
y = x*0.09-45
256*y = x*23.04-11520


这个好,用心记住

eduhf_123 发表于 2012-3-31 09:50:18

本帖最后由 eduhf_123 于 2012-3-31 10:11 编辑

shafei 发表于 2012-3-30 18:33 static/image/common/back.gif
您这句话对我处理四舍五入提供了一个好方法,学习了!
晚辈看了一半帖子的时候,写的处理方法是这样的:
void display(uint x)
{
      uint y,tmp_0,tmp_1,tmp_2,tmp_3;
      y=9*x-4500;
      tmp_0=y/10000;
      tmp_1=(y%10000)/1000;
      tmp_2=((y%10000)%1000)/100;
      tmp_3=((y%10000)%1000)%100;
      if(tmp_3>=5)
                tmp_2+=1;//以上是我本来的数据处理程序
...

这里是有问题的,考虑在if判断之前tmp_2是9、tmp_3大于5的情况——显示会出错。
-----------------------------------------------------------------------------------------------------
shafei 发表于 2012-3-30 18:33 static/image/common/back.gif
...
现在尝试改成您说的那样:
void display(unsigned short int x)
{
unsigned char y;
y= (9*x-4450) /100//我这个地方没有错误了吧?
...
这里依然有问题——少了个分号。
{:lol:}
-----------------------------------------------------------------------------------------------------
shafei 发表于 2012-3-30 18:33 static/image/common/back.gif
...
这个程序里面只是把误差控制在您说的,没有四舍五入。
...
首先,这个公式是有四舍五入的,注意:
(9*x-4450)/100 = (9*x-4500+50)/100 = 0.09*x-45+0.5
这个0.5就是用做四舍五入处理的;
其次,从上面的推算过程就可以知道,这个公式的推演过程没有引入额外的误差,整个计算过程只有最后的四舍五入处理引入了一个±0.5的误差,而这个误差是没有办法避免的,如果你要用整数存储最后的计算结果的话。
-----------------------------------------------------------------------------------------------------
shafei 发表于 2012-3-30 18:33 static/image/common/back.gif
...
如果按您对六楼的优化:y= INT( (x*23-11392)/256 )
这里面就加入了四舍五入了对吧,误差控制在[-1,0]之间了,同时x最好从501算起
同上,-11392 = -11520+128,所以是有四舍五入的,这没有问题;误差在[-1, 0]之间也没有问题(详见我在20楼上传的附件);但这里加入了四舍五入处理后,x取500也没有问题,因为不会出现“负结果”,6楼的原始算法没有处理四舍五入,程序在输入500的时候本该输出0度,却回因为减法结果为负20(实际被处理成65516)而输出255度。

XA144F 发表于 2012-3-31 11:59:46

Y=0.09*X-45=(9*X-4500)/100=(X-500)*9/100
这样的话就变成X-500再乘以9的运算了,得到结果后除以100——商是整数部分,余数是小数部分,稍微处理一下就是可显示的ASCII字符串。

airfex 发表于 2012-3-31 12:09:33

eduhf_123 发表于 2012-3-29 21:13 static/image/common/back.gif
改良6楼的办法(加入对四舍五入的处理):
y = x*0.09-45
256*y = x*23.04-11520


这种处理方法好,留下自己用了。哈哈,不要版权费吧。

nihao_x 发表于 2012-4-1 14:12:00

不错啊~~`````{:smile:}{:smile:}{:smile:}

GNMXD 发表于 2012-6-21 17:25:06

4楼,
6楼,
10楼
不错的处理方法,,,
受益了,,,
谢谢{:lol:}

wtiechen1969 发表于 2012-6-21 21:13:55

又学到不少东西,mark

jacktau 发表于 2012-6-21 21:31:07

赞一下~~~~~~~~~~~~

xiongpeiyong888 发表于 2012-6-25 12:25:05

真是受教了{:smile:}

r166 发表于 2012-8-6 11:53:46

这个好,收藏了

kxm2008 发表于 2012-8-6 11:55:02

相当厉害

llj1007 发表于 2012-8-6 15:54:26

学习了很不错

lmt50211 发表于 2012-8-22 20:27:05

学习了......

ad00r 发表于 2012-9-20 22:36:20

学习了   

jiang47 发表于 2012-9-20 23:40:06

MARK{:biggrin:}

xts 发表于 2012-9-21 09:00:01

果然是好帖啊!

yuyous 发表于 2012-9-21 09:12:33

学习了                                 

rube 发表于 2012-9-21 13:39:04

{:victory:}{:victory:}{:victory:}

wangyuezhuiyi 发表于 2012-9-21 19:59:53

MARK!MARK! 

lihebb 发表于 2012-9-22 15:02:49

eduhf_123 发表于 2012-3-30 15:33 static/image/common/back.gif
值得一提的是,8楼这种处理四舍五入的办法效率太低了。
当我们需要对一个除法运算的商进行四舍五入时,只 ...

这招很不错的学一个{:handshake:}

吴杰 发表于 2012-9-27 15:36:47

不错哦,先顶一下!

Jmhh247 发表于 2014-4-9 09:35:00

不错,学习了,收藏

noley 发表于 2014-4-9 10:06:48

受教啦!原来有这么多技巧!{:smile:}

crazydtone 发表于 2014-4-9 10:27:14

受教了,谢谢各位前辈分享!

赞 +1...

li_thomas 发表于 2014-4-9 10:46:31

这个在没有FPU的片子中需要经常用到,叫做浮点运算定点化。这个向上要考虑溢出的问题,向下要考虑精度的问题。针对你这个具体例子,100y=9x-4500=m, y=(m+50)/100

msm2009 发表于 2014-4-9 18:24:08

非常给力,这是优化代码的常用方法啊

sywh 发表于 2014-4-9 19:24:04

mark,不错哦,单片机处理小数的速度会降到多少?

hz_luping 发表于 2014-4-9 19:46:30

学习了{:handshake:}

ZYBing 发表于 2014-4-9 20:05:33

来学习{:victory:}{:victory:}

guoj 发表于 2014-4-10 06:57:39

mark标记

1米49 发表于 2014-4-10 08:32:10

学习下,谢谢谢谢

zkf0100007 发表于 2014-4-10 12:07:41

好东西,用得着

shjw 发表于 2014-4-10 12:52:33

eduhf_123 的算误差方法很不错,从来没有这样想

dxhdtv 发表于 2014-8-6 22:05:57

谢谢楼主的分享啊!

sunliezhi 发表于 2014-8-6 23:07:47

学习!   

linread 发表于 2014-8-6 23:33:20

也遇到过类似的问题,扩大倍数是比较有效的方法

fgdzypf 发表于 2014-8-7 08:17:27

思路很不错嘛{:biggrin:}

shafei 发表于 2014-8-29 18:36:26

eduhf_123 发表于 2012-3-30 15:20
要善用Excel、善用Excel的公式。

时隔两年之久,我重新来看当年刚读研究生的时候,发的帖子,不仅体会了当初稚嫩的自己,还体会到了大家的热心肠,我是今年6月24号研究生毕业离校了,刚上班做FPGA的采集数据消除白噪声处理,用到了EXCEL处理数据,分析数据和绘图,才深有感触的体会eduhf_123当年内功的深厚,这些年,也见证了自己的成长,谢谢eduhf_123,谢谢大家!

nhw1234 发表于 2014-8-29 19:30:22

y574924080 发表于 2012-3-29 19:48
这个应该对你有帮助

感谢分享啊

eduhf_123 发表于 2014-9-3 18:14:14

shafei 发表于 2014-8-29 18:36
时隔两年之久,我重新来看当年刚读研究生的时候,发的帖子,不仅体会了当初稚嫩的自己,还体会到了大家的 ...

这段时间在深圳出差,时间不由自己安排,所以没怎么上论坛,没有及时回复,请见谅。

功力深厚谈不上了,一点点小技巧而已,算不得什么高深的技术。

我的一点小技巧能对你有帮助,我很开心——因为这让我有种成就感,哈哈!
{:lol:}
页: [1]
查看完整版本: 单片机y=0.09*x-45的数据处理