c - 模运算似乎不适用于所有值的 64 位值

标签 c cpu intel modulo modulus

所以...模运算似乎不适用于所有 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);
}

Output:

-1 % 16492674416640 = -1

在 ISO C 中,% 运算符的定义是 (a/b)*b + a%b == a。此外,对于负数,/ 遵循“向零截断”。

所以-1/164926744166400,因此-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)-1unsigned long long0xFFFFFFFFFFFFFFFF 具有相同的表示形式,这解释了您最终得到的原因max_ll 保持值 -1

关于c - 模运算似乎不适用于所有值的 64 位值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35109230/

相关文章:

c++ - char数组声明中字符串文字周围的大括号有效吗? (例如 char s[] = {"Hello World"})

c - 在 C 中使用 sscanf 将十六进制字符串转换为 bash 的命令参数

c++ - 最大并发 C++ 编译与最大并行项目构建数

c++ - 编译器优化 : g++ slower than intel

intel - 在硬件中断之前如何处理分支预测错误

c++ - Intel CPU 的宏测试 AVX-2 架构是什么?

c - 为什么我不能将 real[0] 分配给 x 的地址

python - 使用来自 python subprocess.call 的参数运行 C 代码时出现问题

java - 只要 ServerSocket 空闲,Linux 上的 Tomcat6 就会使用 100% 的 CPU

assembly - 在 x86 软件中断期间,具体何时进行上下文切换?