【发布时间】:2021-07-14 02:24:27
【问题描述】:
在过去的几天里,我一直在用这段“无害”的代码(最小的可重现示例,更大的模乘例程的一部分):
#include <iostream>
#include <limits>
using ubigint = unsigned long long int;
using bigint = long long int;
void modmul(bigint a, bigint b, ubigint p) {
ubigint ua = a < 0 ? -a : a;
ubigint ub = b < 0 ? -b : b;
ua %= p;
ub %= p;
std::cout << "ua: " << ua << '\n';
}
int main() {
bigint minbigint = std::numeric_limits<bigint>::min();
bigint maxbigint = std::numeric_limits<bigint>::max();
std::cout << "minbigint: " << minbigint << '\n';
std::cout << "maxbigint: " << maxbigint << '\n';
modmul(minbigint, maxbigint, 2314); // expect ua: 2036, got ua: 0
}
我正在使用从 Homebrew 安装的 clang 12.0 在 macOS 11.4 上进行编译
clang version 12.0.0
Target: arm64-apple-darwin20.5.0
Thread model:posix
InstalledDir: /opt/homebrew/opt/llvm/bin
当使用clang -O1 编译时,程序会输出预期的结果(在本例中,2036,我使用Wolfram Mathematica、Mod[9223372036854775808, 2314] 进行了检查,这是正确的)。但是,当我使用clang -O2 或clang -O3(完全优化)编译时,不知何故变量ua 被清零(它的值变为0)。我在这里完全不知所措,不知道为什么会发生这种情况。 IMO,这段代码中没有UB,也没有溢出或任何可疑的东西。非常感谢任何建议,或者您是否可以在您身边重现该问题。
PS:代码在任何其他平台(包括 Windows/Linux/FreeBSD/Solaris)上的行为都符合预期,可以使用任何编译器组合。我只在带有 clang 12 的 Apple M1 上收到此错误(未在 M1 上使用其他编译器进行测试)。
【问题讨论】:
-
好吧,你说没有溢出,但否定最负面的 bigint 有点阴暗。如果您在 否定之前转换为 ubigint,这仍然会发生吗? (否定无符号整数毕竟是安全的)
-
@harold Harold,你是金子!这似乎确实是问题所在。你可以发布一个答案吗?这很可能是 C(由 C++ 继承)的那些非常黑暗的角落之一。坦率地说,我不知道这是否是 UB……我想知道我是否会完全理解无符号与 C++ 中的有符号。
-
@harold 有趣的是即使使用
std::abs也会给出错误的答案,即ubigint ua = std::abs(a)而a==minbigint。 -
@vsoftco 使用
-fsanitize=undefined,您会立即看到错误godbolt.org/z/b49zx1dKa。在这种情况下,您也可以使用静态分析器 -
@phuclv 谢谢!我实际上正在使用它,
clang++ -fsanitize=undefined -O3 -Wall -Wextra test.cpp,不幸的是,我的平台上没有收到任何警告。
标签: c++ clang++ apple-m1 integer-arithmetic unsigned-long-long-int