我在C中有一个数组,我希望以类似于循环缓冲区的方式进行寻址,例如:a[-1]
将返回数组的最后一个元素.
为了做到这一点,我尝试使用模运算(显然),问题是,当涉及负数时,我得到了相当奇怪的结果:
-1 % 4 = -1
-1 % 4U = 3
Run Code Online (Sandbox Code Playgroud)
到现在为止还挺好.
-1 % 4000 = -1
(-1+4000U) % 4000U = 3999
(-1) % 4000U = 3295
Run Code Online (Sandbox Code Playgroud)
问题:值(3295)确实适用于来自C标准(6.5.5#6)的(a/b)*b + a%b shall equal a, truncated towards zero
(for a=-1, b=4000
)所以它本身不是一个bug ,但为什么标准是这样定义的?!当然,这里必须有一些逻辑......
我如何写a%b
以获得负面的合理结果a
(因为(a+b)%b
停止工作时abs(a)>b
)?
测试应用:
#include <stdio.h>
int main(int argc, char **argv) {
int i=0;
#define MAX_NUM 4000U
int weird = (i-1)%MAX_NUM;
printf("%i\n", weird);
printf("%i\n", (i-1+MAX_NUM))%MAX_NUM);
printf("a: %i, b: %i, a from equation: %i\n", i-1, MAX_NUM,
((i-1)/MAX_NUM)*MAX_NUM + weird);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
C中的算术始终(在使用位移运算符时有些奇怪之处)在执行操作之前将所有操作数提升为公共类型.从而:
(-1) % 4000U
Run Code Online (Sandbox Code Playgroud)
被提升为(假设32位整数):
0xffffffffu % 4000u
Run Code Online (Sandbox Code Playgroud)
产生3295.
如果要对可能为负的阵列偏移使用模运算,则首先需要放弃在偏移上使用无符号算术.因此,您的结果现在会在范围-MAX_NUM+1
来MAX_NUM-1
,由于C中的丑陋符号整数除法和余数的定义.如果代码不是性能关键,只需添加if (result<0) result+=MAX_NUM;
并完成它.如果你真的需要避开分支(并且你已经测量确定你需要避免它),那么再问一下如何优化这个计算,我自己或者比我更聪明的人肯定能够提供帮助.:-)
正如6.5.3所说,"通常的算术转换是在操作数上执行的." 就你的例子而言:
(-1) % 4000U
Run Code Online (Sandbox Code Playgroud)
这意味着将-1转换为unsigned int
.因此,您的-1实际上被解释为4294967295 ...其余部分正是您所看到的:3295.
"常用算术转换"在6.3.1.8中描述.
归档时间: |
|
查看次数: |
1854 次 |
最近记录: |