我的基本问题是如何使用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'd float
作为基类型编写的.
更改为double
在两个平台上获得非常相似的结果但不幸的是数字是不可接受的,所以我们不能采取那么简单的方法.
Mac代码是使用CodeWarrior编译的,只是关闭FMADD和FMSUB指令的生成对创建的数字产生了极大的影响.所以,我的出发点是搜索看起来最相似的Visual Studio(2008)选项 - 确保使用融合添加.我们怀疑关键在于编译器在计算中分配中间存储的行为
目前,最好的结果是通过启用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-Lint.该程序运行并产生一些可信的输出,只是不够好.
更新:
朋友的一个有趣的评论: 一种可能性是PPC有大量的临时寄存器可以存储64位中间值,而x86代码可能必须卸载并重新加载FPU(截断为4个字节并且丢失精度).
这可能是SSE2更好地工作的原因(IIRC)它有更多的寄存器和更多的保留中间值的范围.
一种可能性 - 您的代码可以编译为64位吗?x64模式还有更多的中间寄存器和更好的FP指令,因此在设计和执行时可能更接近PPC.
64位构建的初始测试实际上越来越接近了,正如他所建议的那样(我首先认为它超调,但这是由于不正确的建模设置).
最终决议
我确信任何对这个主题感兴趣的人都足够强迫他们想知道最终这一切是如何成功的.该软件已完成并提供一致的数字结果.我们从未能够获得所有算法来向Mac提供相同的结果,但它们足够接近统计上可接受的结果.鉴于处理由专家用户指导选择感兴趣的区域并且用户输入部分地反应模型的进展,首席科学家认为这是可接受的(这不是一夜之间的决定!).剩余的数值差异完全在决定不同临床结果的范围内,因此在测试中未发现不同的诊断.