9015|8

82

帖子

0

TA的资源

一粒金砂(初级)

楼主
 

【注意】折腾一晚上,得出这样一个结论:IAR提供的标准库函数<math.h>有BUG? [复制链接]

请大家看看,两个功能相同的程序,最后的执行结果有很大误差!

恳请版主及各路大侠帮我分析一下,给个合理的解释。谢谢!!

程序设计目的:将1个以4字节(紧缩)BCD码格式连续存放的数组,转换为无符号长整数。
{0x10,0x32,0x54,0x76} ――> 76543210
编译环境:IAR EW 3.10A

程序1:

#include <msp430x44x.h>

typedef unsigned char BYTE;
unsigned int BCD_to_BIN(BYTE k);
unsigned long int LJSL(BYTE num1);
unsigned long int sl;

//主函数为测试这个转换函数
void main(void)
{
while (1)
{
sl =LJSL(0x01);
_NOP();
}
}

unsigned long int LJSL(BYTE num1)
{
BYTE LJ,i,Adds=0x00;
unsigned long int digit=0,dd;
LJ = num1;
Adds += LJ - 1;
dd = 1;
for (i=0;i<4;i++)
{
LJ = BCD_to_BIN(Adds);
digit += LJ * dd;
dd *= 100;
Adds++;
}
return(digit);
}

unsigned int BCD_to_BIN(BYTE k)
{
BYTE digit=0x00;
BYTE DataL,DataH;
BYTE SL[4] = {0x10,0x32,0x54,0x76};
DataH = SL[k] & 0xF0;
DataL = SL[k] & 0x0F;
DataH >>= 4;
digit = DataH * 10 + DataL;
return(digit);
}

以上《程序1》可以正确的将数组“SL[4] = {0x10,0x32,0x54,0x76}”转换为长整数sl=76543210。但我开始不是这样设计程序,而是使用了IAR提供的标准库函数中的求幂函数(调用头文件math.h中的“dou××e pow(dou××e arg1,dou××e arg2)”)进行运算,程序如下:

程序2:
#include <msp430x44x.h>
#include <math.h>

typedef unsigned char BYTE;
unsigned int BCD_to_BIN(BYTE k);
unsigned long int LJSL(BYTE num1);
unsigned long int sl;

//主函数为测试这个转换函数
void main(void)
{
while (1)
{
sl =LJSL(0x01);
_NOP();
}
}

unsigned long int LJSL(BYTE num1)
{
BYTE LJ,i,Adds=0x00;
unsigned long int digit=0,dd;
LJ = num1;
Adds += LJ - 1;

dd = 100;//这里和《程序1》不同

for (i=0;i<4;i++)
{
LJ = BCD_to_BIN(Adds);

digit += LJ * pow(dd,i);//这里和《程序1》不同,使用求幂函数

Adds++;
}
return(digit);
}

unsigned int BCD_to_BIN(BYTE k)
{
BYTE digit=0x00;
BYTE DataL,DataH;
BYTE SL[4] = {0x10,0x32,0x54,0x76};
DataH = SL[k] & 0xF0;
DataL = SL[k] & 0x0F;
DataH >>= 4;
digit = DataH * 10 + DataL;
return(digit);
}

上面《程序2》运行在 LJSL(BYTE num1)函数中的for循环中,当i=0~2时,运行结果正确,当i≥3以后,执行完“pow(dd,i)”后,计算出现莫名其妙的无规律数据!

说明一下,在调试《程序2》中,涉及pow函数运算的相关变量类型无论是long int、float、dou××e结果都一样。

谁能解释这个问题?,真的是“dou××e pow(dou××e arg1,dou××e arg2)”的BUG?
有兴趣的大侠不妨将此2程序编译一下运行,比较二者的运行结果。我百思不得其解,EW的库函数使用可靠吗???

最新回复

“浮点数是不能表示精确的数”这个谁都知道,我理解的是误差应该反映在小数部分若干位上。现在的问题是该函数程序其误差表现在整数部分的十位上了。对于象pow这种求幂函数的程序,指数只能在0~2之间可以保证幂函数的整数部分计算正确,那这种函数程序还有多大用处?连普通的函数计算器的精度都达不到,是不是很垃圾。  详情 回复 发表于 2005-5-20 18:02
 
点赞 关注

回复
举报

72

帖子

0

TA的资源

一粒金砂(初级)

沙发
 
没人知道原因?顶一下。
 
 

回复

58

帖子

0

TA的资源

一粒金砂(初级)

板凳
 
我不知道pow是否有bug 不过你的程序还真难看!
LJ定义为BYTE 类型,但是接受unsigned int 类型数据;pow返回dou××e类型,但用unsigned long类型接收 浮点跟整形转换可能丢失位,有符号跟无符号转换可能引起错误!
 
 
 

回复

84

帖子

0

TA的资源

一粒金砂(初级)

4
 
我以前测试过,3.10A中,dou××e 类型的数据绝对不能用,会报错的,2.10A就OK.
我想3.10A 可能有些问题,如果你有2.10A版本的话,讲你的程序在2.10A中测试,,看到底是math.h 有问题,还是3.10A 有问题!!!!
 
 
 

回复

64

帖子

0

TA的资源

一粒金砂(初级)

5
 
呵呵,对不起,没有注释,是难得看明白!不过还是要感谢楼上二位的回复。
有必要把程序解释一下,同时说明我的算法。其实,《程序1》我个人认为对有些同行也许有用,这已经通过调试了,贴出来一是共享,二是借此想通过讨论相互提高。
该程序是为了满足建设部“三表标准”中,对累计流量运算进行数据转换而设计的。“三表标准”中规定累计流量为4字节BCD码(另外加1字节单位代码0x2C,即立方米),传送方向是低位在前。假设当前流量为76543210立方米,该数据在内存中的BCD码以数组存放为:SL[4]={0x10,0x32,0x54,0x76}。
为了将数组SL转换为一个无符号长整数,拟调用unsigned long int LJSL(BYTE num1)完成。
函数LJSL的形参num1是欲转换的数组首字节指针(为便于调试跟踪,设置为char类型。实际使用时应该为指针型参数)。
由于紧缩型BCD码是以半字节表示一位十进制数,十进制的“35”以BCD码表示为0x35。故而我设计了一个将一字节BCD数据(表示两位十进制数)转换为二进制数据的函数“unsigned int BCD_to_BIN(BYTE k)”。其转换算法为:

unsigned int BCD_to_BIN(BYTE k)//形参k为欲转换的BCD数组下标变量
{
......
DataH = 0x35 & 0xF0; //1、截取BCD字节高4位――>DataH
DataL = 0x35 & 0x0F ;//2、截取BCD字节低4位――>DataL
DataH >>= 4; //3、BCD字节高4位右移到低半字节
digit = DataH * 10 + DataL;//组成十进制数“35”
return(digit);//带转换成功的十进制数据返回
}

上述函数在LJSL函数中调用,如下:
unsigned long int LJSL(BYTE num1)
{
BYTE LJ,i,Adds=0x00;
unsigned long int digit=0,dd;
LJ = num1;
Adds += LJ - 1;//待转换BCD数组下标变量赋初始值。这样设计是为实际应用时取得2LC08中该数据的首地址。本例为模拟调试,故Adds=0x00,
dd = 1;//十进制转换系数:100的“i”次幂(i=0,1,2,3)。初始值:dd=1
for (i=0;i<4;i++)
{
LJ = BCD_to_BIN(Adds);//转换BCD数组中第i个字节为二进制数――>LJ
digit += LJ * dd;//LJ乘以当前十进制转换系数dd,并累加到中间转换结果变量“digit”中
dd *= 100;//十进制转换系数dd = 100^i(这里用“100^i”表示100的“i”次幂)
Adds++;//调整BCD数组下标变量指针,指向下一个转换数据
}
return(digit);//完成转换,带最终结果返回
}

程序2与程序1的转换算法是一样的,唯一不同的是在LJSL函数中将“digit += LJ * dd;”LJ乘以当前十进制转换系数dd,并累加到中间转换结果变量“digit”中使用标准库函数pow来计算100的 i 次幂,没想到当 i 从0~2变化时转换结果正确(digit = 543210),而当 i =3时,执行“digit += LJ * pow(100,i)”后digit=76543224 !按理应该为digit=76543210才对。 为什么会产生这么大的误差?

对了,昨天给的程序要关闭看门狗功能,否则程序要跑飞。这也是一个困惑!郁闷中~~~
 
 
 

回复

84

帖子

0

TA的资源

一粒金砂(初级)

6
 
pow(dd,i)函数好像是有数值范围的,我测试了一下,输出不能大于7位,否则有误差了。
测试程序:
#include <msp430x44x.h>
#include <math.h>
void main(void)
{
unsigned int LJ=10,i=0,dd=100;
unsigned long int digit=0;
digit += LJ * pow(dd,i);//0
LJ = 32,i=1;
digit += LJ * pow(dd,i);//1
LJ = 54,i=2;
digit += LJ * pow(dd,i);//2
LJ =76,i=3;
digit += LJ * pow(dd,i);//3
LJ = 98,i=4;
digit += LJ * pow(dd,i);//4
_NOP();
}

i=3时就有误差了,望高手解释
 
 
 

回复

87

帖子

0

TA的资源

一粒金砂(初级)

7
 
引用:

pow(dd,i)函数好像是有数值范围的,我测试了一下,输出不能大于7位,否则有误差了。
测试程序:
#include <msp430x44x.h>
#include <math.h>
void main(void)
{
unsigned int LJ=10,i=0,dd=100;
unsigned long int digit=0;
digit += LJ * pow(dd,i);//0
LJ = 32,i=1;
digit += LJ * pow(dd,i);//1
LJ = 54,i=2;
digit += LJ * pow(dd,i);//2
LJ =76,i=3;
digit += LJ * pow(dd,i);//3
LJ = 98,i=4;
digit += LJ * pow(dd,i);//4
_NOP();
}

i=3时就有误差了,望高手解释
对,就是这个问题!但pow函数本身是双精度浮点函数啊!若把形参中的dd换成常数,误差好一点(i=3时)。所以各位使用这类库函数一定要慎重,最好在实际使用的值域范围内全程验证!
在Keil C51中没有这个问题。
 
 
 

回复

81

帖子

0

TA的资源

一粒金砂(初级)

8
 
弄清浮点数表示方法,浮点数是不能表示精确的数的,特别是数值大的时候。
 
 
 

回复

87

帖子

0

TA的资源

一粒金砂(初级)

9
 
“浮点数是不能表示精确的数”这个谁都知道,我理解的是误差应该反映在小数部分若干位上。现在的问题是该函数程序其误差表现在整数部分的十位上了。对于象pow这种求幂函数的程序,指数只能在0~2之间可以保证幂函数的整数部分计算正确,那这种函数程序还有多大用处?连普通的函数计算器的精度都达不到,是不是很垃圾。
 
 
 

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/7 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表