所以...模运算似乎不适用于所有 1 的 64 位值。
这是我设置边缘情况的 C 代码:
#include <stdio.h>
int main(int argc, char *argv[]) {
long long max_ll = 0xFFFFFFFFFFFFFFFF;
long long large_ll = 0x0FFFFFFFFFFFFFFF;
long long mask_ll = 0x00000F0000000000;
printf("\n64-bit numbers:\n");
printf("0x%016llX\n", max_ll % mask_ll);
printf("0x%016llX\n", large_ll % mask_ll);
long max_l = 0xFFFFFFFF;
long large_l = 0x0FFFFFFF;
long mask_l = 0x00000F00;
printf("\n32-bit numbers:\n");
printf("0x%08lX\n", max_l % mask_l);
printf("0x%08lX\n", large_l % mask_l);
return 0;
}
输出显示:
64-bit numbers:
0xFFFFFFFFFFFFFFFF
0x000000FFFFFFFFFF
32-bit numbers:
0xFFFFFFFF
0x000000FF
这是怎么回事?
为什么模运算不能对全 1 的 64 位值起作用,但它可以对全 1 的 32 位值起作用?
这是 Intel CPU 的错误吗?或者以某种方式使用 C?还是其他原因?
更多信息
我在一台配备 Intel i5-4570S CPU 的 Windows 10 机器上。我使用了 Visual Studio 2015 中的 cl
编译器。
我还通过进入程序员模式使用 Windows 计算器应用程序(版本 10.1601.49020.0)验证了这个结果。如果您尝试对 0xFFFF FFFF FFFF FFFF
取模,它只会返回自身。
指定无符号与有符号似乎没有任何区别。
请赐教:)我确实有这个操作的用例...所以它不是纯粹的学术。
最佳答案
您的程序导致 undefined behaviour通过使用错误的格式说明符。
%llX
只能用于 unsigned long long
。如果您使用正确的说明符 %lld
,那么表面上的谜团就会消失:
#include <stdio.h>
int main(int argc, char* argv[])
{
long long max_ll = 0xFFFFFFFFFFFFFFFF;
long long mask_ll = 0x00000F0000000000;
printf("%lld %% %lld = %lld\n", max_ll, mask_ll, max_ll % mask_ll);
}
-1 % 16492674416640 = -1
在 ISO C 中,%
运算符的定义是 (a/b)*b + a%b == a
。此外,对于负数,/
遵循“向零截断”。
所以-1/16492674416640
是0
,因此-1 % 16492674416640
必须是-1
才能使上面的公式有效。
正如评论中所讨论的,以下行:
long long max_ll = 0xFFFFFFFFFFFFFFFF;
导致 实现定义 行为(假设您的系统将 long long
作为 64 位类型)。常量 0xFFFFFFFFFFFFFFFF
的类型为 unsigned long long
,它超出了 long long
的范围,其最大允许值为 0x7FFFFFFFFFFFFFFFF
.
当对有符号类型进行超出范围的赋值时,行为是实现定义的,这意味着编译器文档必须说明发生了什么。
通常,这将被定义为生成在 long long
范围内的值,并且具有与 unsigned long long
相同的表示> 常有。在 2 的补码中,(long long)-1
与 unsigned long long
值 0xFFFFFFFFFFFFFFFF
具有相同的表示形式,这解释了您最终得到的原因max_ll
保持值 -1
。
关于c - 模运算似乎不适用于所有值的 64 位值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35109230/