我的基本问题是如何使 x86 上的浮点运算表现得像 PowerPC,从 Classic MacOS (CodeWarrior) 到 Windows (VS 2008)。
有问题的代码有很多,有一堆算法,这些算法高度迭代并且对数值非常敏感。
典型的复杂线是:
Ims_sd = sqrt((4.0*Ams*sqr(nz)-8.0*(Ams+Dms)*nz+12.0*sqr(Ams)) /
(4.0*sqr(Ams)*(sqr(nz)-1)) -
sqr(Ims_av))*sqrt(nz-1);
它是使用 typedef'dfloat
作为基本类型编写的。
更改为double
在两个平台上得到非常相似的结果,但不幸的是这些数字是不可接受的,所以我们不能采取那么简单的方法。
Mac 代码是使用 CodeWarrior 编译的,只是关闭 FMADD 和 FMSUB 指令的生成对创建的数字产生了巨大影响。因此,我的出发点是搜索看起来最相似的 Visual Studio (2008) 选项 - 确保使用了 fused add。我们怀疑关键在于编译器在计算中分配中间存储的行为
目前,通过启用 SSE2 和/fp:fast
. 启用内在函数会导致值偏离 Mac 值。
/ fp开关文档说只/fp:strict
关闭融合添加行为。
MSDN谈到“在 LIBC.LIB、LIBCMT.LIB 或 MSVCRT.LIB 之前”链接 FP10.OBJ。保证64位精度。我显然已经通过在链接器输入字段上指定 FP10.OBJ 来实现这一点(详细的链接器输出在 MSVCRTD.lib 之前显示它)。
我还通过调用设置了 64 位精度
_controlfp_s(&control_word, _PC_64, MCW_PC);
在 DllMain 中。
请注意,问题不是由于平台之间浮点异常处理的差异,也不是由于 PowerPC 允许除以零整数(仅返回零)的(令人愉快的)方式,因为这些区域已经过审计和解决,非常感谢PC-皮棉。该程序运行并产生了一些看似合理的输出,但还不够好。
更新:
一位朋友的有趣评论: 一种可能是 PPC 有大量临时寄存器,可以存储 64 位中间值,而 x86 代码可能必须卸载和重新加载 FPU(截断到 4 个字节并丢失精度)。
这可能是 SSE2 工作得更好的原因,因为 (IIRC) 它有更多的寄存器和更多的保留中间值的空间。
一种可能性 - 您的代码可以编译为 64 位吗?x64 模式还有更多的中间寄存器,以及更好的 FP 指令,因此在设计和执行上可能更接近 PPC。
正如他所建议的那样,使用 64 位构建的初始测试实际上更接近了(我最初认为它过头了,但这是由于建模设置不正确造成的)。
最终决议
我敢肯定,任何对这个话题感兴趣的人都足够痴迷,他们想知道这一切最终是如何解决的。该软件已完成并提供一致的数字结果。我们永远无法获得所有算法来为 Mac 提供相同的结果,但它们足够接近,可以在统计上接受。鉴于处理是由专家用户选择感兴趣的区域指导的,并且用户输入对模型的进展有部分反应,首席科学家认为这是可以接受的(这不是一夜之间的决定!)。剩余的数字差异完全在决定不同临床结果的范围内,因此在测试中没有看到不同的诊断。