打印

99.99%可能性,KEIL BUG

[复制链接]
楼主: ayb_ice
手机看帖
扫描二维码
随时随地手机跟帖
61
本帖最后由 刘前辈 于 2011-9-28 18:57 编辑

上面带符号除法运算,C51调用专用除法库函数,余数在B。

无符号除法才用逻辑移位。否则符号位怎么处理?

使用特权

评论回复
62
mohanwei| | 2011-9-28 19:02 | 只看该作者
常数/非int型变量前加强制转换:(int):lol

使用特权

评论回复
63
LG5A_104| | 2011-9-28 22:01 | 只看该作者
我也有碰到过哎

使用特权

评论回复
64
t.jm| | 2011-9-29 08:13 | 只看该作者
搞编程的对BUG到底有没有清晰的定义?
我问你们:哪个BUG不是超出设计者预期?
又比如windows的BUG会被病毒设计者利用,你不利用它这个BUG又有什么伤害?你不利用别人会利用啊!
你不做/4运算别人有可能会用啊!
依次展开想象,今天你们用的有效计算方法,哪天就可能失效的,比如一再被大家提到的:
int/4,这只取决于,keil有没有兴趣把它优化为移位运算!你们所附加的强制类型转换会不会出错也取决于keil
会不会自作聪明的优化!

使用特权

评论回复
65
ejack| | 2011-9-29 08:14 | 只看该作者
在9.03上实验了一下,的确如楼主所说,我们可以认为这是一个bug。
问题的根源就在于对于字符型变量除以2的整数幂时,编译器会按照右移位的方式进行“快捷”运算,这样做的后果便是对有符号数的舍入方向将不一致。
而取余运算、以及非单字节运算,编译器都会调用库函数,因此不会存在这种问题。
楼主不介意的话,我去把这个bug向官方反馈一下,免得今后害人。

使用特权

评论回复
66
ayb_ice|  楼主 | 2011-9-29 08:14 | 只看该作者
本帖最后由 ayb_ice 于 2011-9-29 08:17 编辑
其实不是keil的错。
以8086为例:你用DIV,还是IDIV,同样的 -5/4,将会得到2个结果。关键是。2个结果都是对的: 注意商不一样,余数就不一样!

-5/4= -1……-1;
-5/4= -2……+3;

LZ的题目应写成下面,告诉编译 ...
刘前辈 发表于 2011-9-28 18:44

改成
"x=z/(signed)4"
实际是等于
"x=z/(signed int)4"
这时结果当然对了,一个是char型除法了,一个是整型除法了
真正的BUG就是在此了
-5和4没有超出signed char的数据表示范围,用char除法和int除法应该结果是一样的才对

使用特权

评论回复
67
t.jm| | 2011-9-29 08:28 | 只看该作者
在9.03上实验了一下,的确如楼主所说,我们可以认为这是一个bug。
问题的根源就在于对于字符型变量除以2的整数幂时,编译器会按照右移位的方式进行“快捷”运算,这样做的后果便是对有符号数的舍入方向将不一致。
而 ...
ejack 发表于 2011-9-29 08:14

这才是一个好人啊!
问题的根源大家都是知道的,就是移位代替真正除法产生的舍入方向不一致问题!
假如库函数的除法移位除法也是同一个舍入方向就没问题了,也就不再依赖于所谓的技巧:"/ >> int ..."
但是居然大多数电工都表现出:这就不是BUG,就如动车相撞、地铁相撞关我们电工屁事啊!!

使用特权

评论回复
68
ayb_ice|  楼主 | 2011-9-29 08:35 | 只看该作者
如果用移位代替有信号除法的话
结果至少是-1(被除数是负数的情况下)

使用特权

评论回复
69
highgear| | 2011-9-29 08:57 | 只看该作者
如果一个软件造成了飞机爆炸,动车相撞的事故,那一定是造成的,而不是编译器。把事故归咎于编译器与铁道部的可耻言论没有什么区别。

使用特权

评论回复
70
yhn1973| | 2011-9-29 09:06 | 只看该作者
记得有位编程大佬说“优化会产生不确定性”,看来有一定道理。

使用特权

评论回复
71
yhn1973| | 2011-9-29 09:14 | 只看该作者
以前有个程序是在很老的版本下编译的,十几年来一直都很稳定,后来用最新版本又重新编译了,结果就不稳定了,开始是以为干扰问题,后来发现是优化问题,新版本比老版本优化不一样,过分智能了。

使用特权

评论回复
72
ayb_ice|  楼主 | 2011-9-29 09:16 | 只看该作者
如果一个软件造成了飞机爆炸,动车相撞的事故,那一定是 人 造成的,而不是编译器。把事故归咎于编译器与铁道部的可耻言论没有什么区别。
highgear 发表于 2011-9-29 08:57

这个话有些武断了
编译器也是人做的,有BUG不稀奇,如果是商业编译器,花了钱,是可以向厂家索赔的,这个没有问题的
测试的人也不能保证完全测试OK,当你知道有隐患的话,问题好解决,关键有些隐患不太明显,很难知道,当年INTEL的CPU有浮点运算错误,那你能说是微软不行,用户不行吗

使用特权

评论回复
73
t.jm| | 2011-9-29 09:28 | 只看该作者
以前有个程序是在很老的版本下编译的,十几年来一直都很稳定,后来用最新版本又重新编译了,结果就不稳定了,开始是以为干扰问题,后来发现是优化问题,新版本比老版本优化不一样,过分智能了。 ...
yhn1973 发表于 2011-9-29 09:14

所以我说了,今天int/4是正确的,不代表将来也正确,发现BUG了不抓被咬一口就晚了!
而人啊,似乎是被咬了一口的BUG才算BUG,没咬过自己的不算!

使用特权

评论回复
74
Cortex-M0| | 2011-9-29 10:50 | 只看该作者
呵呵~~~

LS各位高手争论异常激励,有从C定义出发,认定这不能算是一个 bug, 只是超出人们的预期而已。
也有的人一口咬定这是一个bug,  不过,不管怎么样,LS的盆友们都想出了名种方法,希望结果能得到人们预期的那样,但大多数盆友一般是将 /2或 /4转换成 int类型,以避开 keil对 char型变量的优化。
但是,将数据类型转换成 int类型,运算太费时间,程序也比原 char型运算长许多,并不理想~~~

这里向大家推荐两种常用方法,概避开了 keil对 /2或 /4  char型变量的优化,又使用char型运算,运算时间仅比原来理想中增加几个时钟周期,程序也仅比原来增加几个字节,属于比较理想的打补丁,供大家参考~~~

方法1(ayb_ice推荐,特点是直观,但费时和占程序容量略多几个字节)
signed char x,z,a;
z = -5;
a = 4;
x = z/a;

方法2(俺推荐,用负数做除数,再对结果求补,特点是速度更快,费时更少,仅增加程序容量2个字节)
signed char x,z;
z = -5;
x = -z/-4;    // 或写成 x = -(z/-4);  效果一样。

使用特权

评论回复
75
刘前辈| | 2011-9-29 18:30 | 只看该作者
赞成楼主,确实是个BUG!因为z/3,z/5 都能正确调用CDIV (不是IDIV)字节除法库函数。
想了个办法,终于把z/4 能避开编译器移位BUG而用CDIV 函数计算了。
#include<REG52.h>
#include<math.h>

main(void )
{
signed  char x, y,z;
z=-5;
x=z/cabs(4);

y=z%4;
while(1);
}

这里有个基本概念: 2个二进制数的除法A/B,首先要符号位一致!然后才能进行运算。
所以一个负数z/+4,根本不可能用移位>>2来得到正确值;除非先把z转换为正整数cabs(z)。
x=cabs(z)/4;   也是对的。

未命名a.JPG (155.65 KB )

未命名a.JPG

使用特权

评论回复
76
刘前辈| | 2011-9-29 18:47 | 只看该作者
无论C?SCDIV (char 字节除法库函数)还是 C?SIDIV (int 除法库函数),首先都要将除数、被除数转化为符号一致的2个数。

使用特权

评论回复
77
刘前辈| | 2011-9-29 19:55 | 只看该作者
本帖最后由 刘前辈 于 2011-9-29 20:06 编辑
74#
方法2(俺推荐,用负数做除数,再对结果求补,特点是速度更快,费时更少,仅增加程序容量2个字节)
signed char x,z;
z = -5;
x = -z/-4;    // 或写成 x = -(z/-4);  效果一样。


千万别推荐方法2。x = -z/-4;  和  x = -(z/-4); 结果不一样!无论是手算还是程序算:
1、  x = -z/-4 = 5 /-4 = -1   余 +1 ;
2、  x = -(z/-4)= - ( 5 / 4) = -1  余 -1 ;





、、

使用特权

评论回复
78
highgear| | 2011-9-29 20:51 | 只看该作者
负整数除法以及移位的舍入方向,历来是一个问题。ansi c 没有对此作出额外说明,而microsoft 对负整数除法的舍入方向作出了规定,所以vc++ 有 +(N-1)的调整。许多c/c++的书籍特地对负整数除法提出告诫,声称舍入方向取决于cpu和编译器。

使用整数运算的程序员必须自己解决舍入的问题。

highgear发表于 2011-1-9 23:54 | 只看该作者 回复 引用 编辑 评分 返回版面 TOP  得分:0
9楼: 这是向上舍入,还是向下舍入的问题, 与 c 语言无关。除法与算数移位的舍入方向不同,所以负数会产生不同结果。在数字信号处理中,对于算数移位操作通常需要加 0.5 补偿, 即 :
   (x + (1<<(N-1))) >> N
这样可以防止算数移位向下舍入带来的误差。例如: -1 /32768,结果为 0, 但 -1 >> 15 = 0xFFFF>> 15 = 0xFFFF = -1, 即有一个相当大的舍入误差。对微弱信号处理时,可以明显地看到加 0.5 补偿后的效果。

使用特权

评论回复
79
highgear| | 2011-9-29 21:07 | 只看该作者
Keil 论坛上也有类似的讨论:

http://www.keil.com/forum/19617/
以及这一个:
http://www.keil.com/forum/14461/

No, it's not a bug at all.

If you get out your copy of K&R and look at the chapter about arithmetic operators, it say that the direction of truncation for the division operator is machine-dependent for negative operands.

This means that the result of (-1 / 2) can be 0 or -1 depending on the hardware and/or the compiler. Neither of the results would be considered an error of the compiler.

If you need a certain direction when truncating the result of dividing a negative number, you will have to do so by hand.
As already noted, the compiler can do whatever it likes for the division. The library function is well-defined - but there is no requirement for it to be the same!

使用特权

评论回复
80
xudc14| | 2011-9-29 22:15 | 只看该作者
:D

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则