所以......模运算似乎并不适用于所有的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 Calculator应用程序(版本10.1601.49020.0)验证了此结果。如果你尝试用任何东西模数0xFFFF FFFF FFFF FFFF
,它就会自行返回。
指定unsigned vs signed didn似乎没有任何区别。
请赐教:)我实际上确实有这个操作的用例...所以它不是纯粹学术性的。
答案 0 :(得分:3)
您的程序使用错误的格式说明符导致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
的范围,其最大允许值为0x7FFFFFFFFFFFFFFF
。
当对签名类型进行超出范围的赋值时,行为是实现定义的,这意味着编译器文档必须说明会发生什么。
通常,这将被定义为生成long long
范围内的值,并且具有与unsigned long long
常量相同的表示。在2的补码中,(long long)-1
具有与unsigned long long
值0xFFFFFFFFFFFFFFFF
相同的表示形式,这解释了为什么最终max_ll
保留了值-1
。
答案 1 :(得分:2)
实际上确实会将这些值定义为signed
还是unsigned
:
#include <stdio.h>
#include <limits.h>
int main(void) {
#if ULLONG_MAX == 0xFFFFFFFFFFFFFFFF
long long max_ll = 0xFFFFFFFFFFFFFFFF; // converts to -1LL
long long large_ll = 0x0FFFFFFFFFFFFFFF;
long long mask_ll = 0x00000F0000000000;
printf("\n" "signed 64-bit numbers:\n");
printf("0x%016llX\n", max_ll % mask_ll);
printf("0x%016llX\n", large_ll % mask_ll);
unsigned long long max_ull = 0xFFFFFFFFFFFFFFFF;
unsigned long long large_ull = 0x0FFFFFFFFFFFFFFF;
unsigned long long mask_ull = 0x00000F0000000000;
printf("\n" "unsigned 64-bit numbers:\n");
printf("0x%016llX\n", max_ull % mask_ull);
printf("0x%016llX\n", large_ull % mask_ull);
#endif
#if UINT_MAX == 0xFFFFFFFF
int max_l = 0xFFFFFFFF; // converts to -1;
int large_l = 0x0FFFFFFF;
int mask_l = 0x00000F00;
printf("\n" "signed 32-bit numbers:\n");
printf("0x%08X\n", max_l % mask_l);
printf("0x%08X\n", large_l % mask_l);
unsigned int max_ul = 0xFFFFFFFF;
unsigned int large_ul = 0x0FFFFFFF;
unsigned int mask_ul = 0x00000F00;
printf("\n" "unsigned 32-bit numbers:\n");
printf("0x%08X\n", max_ul % mask_ul);
printf("0x%08X\n", large_ul % mask_ul);
#endif
return 0;
}
生成此输出:
signed 64-bit numbers:
0xFFFFFFFFFFFFFFFF
0x000000FFFFFFFFFF
unsigned 64-bit numbers:
0x000000FFFFFFFFFF
0x000000FFFFFFFFFF
signed 32-bit numbers:
0xFFFFFFFF
0x000000FF
unsigned 32-bit numbers:
0x000000FF
0x000000FF
64位十六进制常量0xFFFFFFFFFFFFFFFF
在存储到-1
时具有值long long
。这实际上是由于超出范围转换为签名类型而定义的实现,但在英特尔处理器上,对于当前编译器,转换只保留相同的位模式。
请注意,您没有使用<stdint.h>
中定义的固定大小整数:int64_t
,uint64_t
,int32_t
和uint32_t
。 long long
类型在标准中指定为具有至少64位,在Intel x86_64上,它们具有,long
具有至少32位,但对于相同的处理器,大小在环境之间不同: Windows 10中的32位(即使在64位模式下)和MaxOS / 10和linux64上的64位。这就是为什么long
unsigned
和signed
可能会产生相同结果的LLONG_MIN / -1
情况的惊人行为的原因。它们不在Windows上,但它们在Linux和MacOS中运行,因为计算是以64位完成的,这些值只是正数。
另请注意,由于带符号的算术溢出,LLONG_MIN % -1
和1 / 0
都会调用未定义的行为,并且在Intel PC上不会忽略此行为,它通常会触发未捕获的异常并退出程序,就像1 % 0
和/**
* @param uniqueList
* @param permutationSize
* @param permutation
* @param only Only show the permutation of permutationSize,
* else show all permutation of less than or equal to permutationSize.
*/
public static void my_permutationOf(List<Integer> uniqueList, int permutationSize, List<Integer> permutation, boolean only) {
if (permutation == null) {
assert 0 < permutationSize && permutationSize <= uniqueList.size();
permutation = new ArrayList<>(permutationSize);
if (!only) {
System.out.println(Arrays.toString(permutation.toArray()));
}
}
for (int i : uniqueList) {
if (permutation.contains(i)) {
continue;
}
permutation.add(i);
if (!only) {
System.out.println(Arrays.toString(permutation.toArray()));
} else if (permutation.size() == permutationSize) {
System.out.println(Arrays.toString(permutation.toArray()));
}
if (permutation.size() < permutationSize) {
my_permutationOf(uniqueList, permutationSize, permutation, only);
}
permutation.remove(permutation.size() - 1);
}
}
。
答案 2 :(得分:1)
尝试将unsigned
放在long long
之前。作为带符号的数字,你的0xFF ... FF在大多数平台上实际为-1。
此外,在您的代码中,您的32位数字仍然是64位(您也将它们声明为long long
)。