在将一些复杂的代码移植到macOS/arm64时,我遇到了一些问题,并最终得到了以下简单的代码来展示与macOS/x86_64的不同行为(使用conda-forge中的本机osx/arm64 clang版本14.0.6,并进行x86_64的交叉编译):
#include "assert.h"
#include "stdio.h"
int main()
{
double y[2] = {-0.01,0.9};
double r;
r = y[0]+0.03*y[1];
printf("r = %24.26e\n",r);
assert(r == 0.017);
}
在arm64上的结果是
$ clang -arch arm64 test.c -o test; ./test
Assertion failed: (r == 0.017), function main, file test.c, line 9.
r = 1.69999999999999977517983751e-02
zsh: abort ./test
在x86_64上的结果是
$ clang -arch x86_64 test.c -o test; ./test
r = 1.70000000000000012212453271e-02
$
测试程序也在x86_64机器上编译/运行过,结果与上述(在arm64上交叉编译并使用Rosetta运行)相同。
实际上,arm64的结果与以IEEE754数值解析和存储的1.7不完全相等,并不重要,而是与x86_64的表达式值不同。
更新1:
为了检查可能存在的不同约定(例如舍入模式),以下程序已在两个平台上进行了编译和运行。
#include <iostream>
#include <limits>
#define LOG(x) std::cout << #x " = " << x << '\n'
int main()
{
using l = std::numeric_limits<double>;
LOG(l::digits);
LOG(l::round_style);
LOG(l::epsilon());
LOG(l::min());
return 0;
}
它产生相同的结果:
l::digits = 53
l::round_style = 1
l::epsilon() = 2.22045e-16
l::min() = 2.22507e-308
因此,问题似乎出在其他地方。
更新2:
如果有帮助的话:在arm64下,使用该表达式得到的结果与使用refBLAS ddot函数调用向量{1,0.03}和y得到的结果相同。
更新3:
工具链似乎是原因。使用macOS 11.6.1的默认工具链:
mottelet@portmottelet-cr-1 ~ % clang -v
Apple clang version 13.0.0 (clang-1300.0.29.30)
Target: arm64-apple-darwin20.6.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
给出相同的结果,无论是哪种架构!所以问题似乎出在我正在使用的实际工具链上:我使用的是conda软件包的1.5.2版本(我需要conda作为软件包管理器,因为我正在构建的应用程序有很多依赖项,而conda可以提供给我)。
使用-v选项会显示一堆编译标志,最终可能会怀疑哪一个?
-O0
到-O3
任何选项都得到了1.70000000000000012212453271e-02
的结果。 - Siguza1.70000000000000012212453271e-02
的结果,对于任何从-O0
到-O3
的选项都是如此。 - Siguza-O0
到-O3
的任何选项都会得到1.70000000000000012212453271e-02
的结果。 - undefined