我有以下代码:
unsigned short wrLine;
unsigned short prev = ((wrLine - 1) % 16);
wrLine = (wrLine + 1) % 16;
生成以下反汇编:
unsigned short prev =((wrLine - 1)%LINES_IN_FIFO);
0041456A movw r3, #25282
0041456E movt r3, #8192
00414572 ldrh r3, [r3]
00414574 uxth r3, r3
00414576 add.w r2, r3, #4294967295
0041457A mov.w r3, #15
0041457E movt r3, #32768
00414582 ands r3, r2
00414584 cmp r3, #0
00414586 bge #10
00414588 add.w r3, r3, #4294967295
0041458C orn r3, r3, #15
00414590 add.w r3, r3, #1
00414594 strh r3, [r7, #4]
wrLine =(wrLine + 1)%LINES_IN_FIFO;
0041463E movw r3, #25282
00414642 movt r3, #8192
00414646 ldrh r3, [r3]
00414648 uxth r3, r3
0041464A add.w r2, r3, #1
0041464E mov.w r3, #15
00414652 movt r3, #32768
00414656 ands r3, r2
00414658 cmp r3, #0
0041465A bge #10
0041465C add.w r3, r3, #4294967295
00414660 orn r3, r3, #15
00414664 add.w r3, r3, #1
00414668 uxth r2, r3
0041466A movw r3, #25282
0041466E movt r3, #8192
有趣的是,如果wrLine为零,则prev将最终等于0xFFFF,而当wrLine为15时,它将最终等于0x0000。知道为什么只有其中一个有效吗?
谢谢, 德万
答案 0 :(得分:3)
short
数据类型将转换为int
。由于您的模数为16
,因此它是int
而不是unsigned
。
如果您不能使用short
,请仅使用unsigned
类型进行mod操作,16U
。
答案 1 :(得分:2)
首先,对于任何计算,短值都会提升为int。编译器将%16
转换为等效的按位操作&15
,因为它可能在目标CPU上更有效。实际上它被转换为& 0x8000000F
:这保持了符号位。结果与零进行比较:如果大于0,则直接使用结果的下半部分;否则所有高位都用0xF的按位OR-NOT设置。这使得结果的符号正确。
如果wrLine
为0,要减去1,该值将提升为带符号的int
类型,计算结果为-1。以上计算-1 % 16 = -1
。当-1以无符号短整数存储时,结果为0xFFFF。
如果wrLine
为15,则加1会计算16,而16%16 = 0。
答案 2 :(得分:0)
尝试此更改
unsigned short wrLine;
unsigned short prev = ((unsigned short)(wrLine - 1) % 16);
wrLine = (wrLine + 1) % 16;
或更好(因为它适用于FIFO_SIZE的所有值)。
int wrLine;
int prev = wrLine-1; if (prev<0) prev = FIFO_SIZE-1;
wrLine++; if (wrLine >= FIFO_SIZE) wrLine = 0;
%
很昂贵,因为它涉及分裂。 &
速度更快,但只适用于2的幂。
根据您的体系结构,条件分支可能有或没有成本(8051微控制器分支成本不高,而x86 / x64处理器有更深的管道和缓存未命中需要担心)。但是,最好让代码工作(并且不会轻易破解)而不是无效的代码。
答案 3 :(得分:0)
我有两条建议。对于这个问题,任何一个都可以工作。两次幂除数的最快值是:
wrLine = (wrLine - 1)&(16-1); /* (wrLine + 1) mod 16 */
尽管有JG的评论,但是这个(a)始终有效,并且(b)当wrLine被签名时,编译器将永远不会生成。另一种方法是永不减去。而是使用:
wrLine = (wrLine + 16 - 1)%16;
只要wrLine最初不是负数,这不会产生负面结果。当除数不 2的幂时,这是使用的模式。
我正在回答一个问题,这个问题的答案对于提问者来说可能已经足够好了,但答案一般都不好。转换为无符号值仅的建议适用于2的幂除数。从有符号到无符号的转换(数字上)相当于增加2的大功率。这相当于仅当除数是2的相等或更小的幂时才加0。
例如:
printf("-1 % 23U = %d", -1 % 16U);
产生
-1 % 23U = 15
...显然不是22.我知道(a mod b)最简洁的表达方式之一是:
(a%b + b)%b /* when b>0 */
(a%b - b)%b /* when b<0 */
那些涉及两个部门,因此使用if或if-else的更长解决方案将运行得更快。答案顶部的解决方案将按固定金额处理所有模块减法的情况。