链接到C中的特定库时,运行时精度损失

时间:2014-11-19 08:41:38

标签: c precision

我有一个奇怪的问题,我不知道如何解决。

我有一个程序,它使用长双打来完成大部分数学运算,到目前为止一直运行良好。最近我想使用MLAPACK,这是一个高精度版本的lapack,它使用double double和quad double类型来进行矩阵求解。不幸的是,当我链接到MLAPACK的库时,我在原始程序中失去了精确度。

IE中。如果我只做一个2个数字的简单总和:

long double a =  50000.55964442486829568679 
long double b =  0.006514624142341807720713032

当我链接MLAPACK时我得到(正确的长双)

long double a + b = 50000.5661590490106374945

当我链接到这个库时,我得到:

long double a + b = 50000.56615904901264002547

即,它们在双精度水平上不同而不是长双精度。

问题是,我不知道如何去解决导致这种变化的原因。我假设在MLAPACK中必须有一个也在原始程序中定义的函数,并且它调用错误的函数,但原始程序很大(而不是由我编写)。

代码在linux系统上编译,MLAPACK库与.so文件链接,所有内容都使用相同版本的gcc / gfortran等进行编译。

我确定这不是最好的问题,但我真的不明白为什么会发生这种情况......任何想法甚至开始寻找解决方案?

干杯

1 个答案:

答案 0 :(得分:0)

我假设您正在为Windows编译程序,为“32位”,而不是“64位”。如果您使用的是Microsoft Visual C,请在开始数学运算之前添加此行:

_control87( _PC_64, _MCW_PC );    /* requires: #include <float.h> */

如果您使用的是其他编译器,则可能需要使用其他功能。

(我有点怀疑你是在使用MSVC,因为它没有“long double”作为一个独特的浮点类型。无论如何你还在使用什么?)

完整解释:

正在发生的事情是CPU内的浮点单元的精度级别已被MLAPACK库中的启动代码更改。

x87 FPU可以以三种模式运行:单精度(24位精度),双精度(53位精度)和扩展精度(64位精度,a / k / a长)双)。在Microsoft Visual C中,精度模式由_control_87内置函数设置;它可能在您的编译器中有所不同。 http://msdn.microsoft.com/en-us/library/e9b52ceh.aspx

通常,精确模式在C运行时库的“启动代码”中设置,每当您构建C程序时都会包含该库。您的程序确实不是以main()开头,而是在C运行时库中有一些其他“入口点”。该入口点的代码设置了所有内容,以便C程序可以运行,然后调用main函数。如果你的程序通常具有长双精度,那意味着入口点函数必须调用_control87(_PC_64, _MCW_PC)来设置64位长双精度控制。

那么当你链接到MLAPACK时为什么会改变?我猜MLAPACK是一个DLL(动态链接库),或者在某些时候它恰好加载DLL。 DLL也有自己的C运行时库(它们比普通的静态库更像是独立的可执行文件) - 特别是如果MLAPACK是用不同的编译器构建的,那么它将拥有一个不同的C运行时库,自己的启动代码。 启动代码将x87 FPU设置为53位(双精度)!

所以答案是:你需要确保在开始进行数学运算之前调用_control87(_PC_64, _MCW_PC)或编译器中的等效项,在程序中设置为“long double”精度。在main中执行此操作可能没问题,这是您做的第一件事。或者可能有必要首先执行涉及MLAPACK的某些,以确保MLAPACK完全启动。就像你可能会反转一个1x1矩阵,一种愚蠢的东西,然后设置为64位精度 - 基本上你正在撤消MLAPACK的C运行时库启动造成的损害。

注意:在Windows上,64位程序根本不使用x87浮点单元,因此它们永远不会具有“long double”精度。这就是为什么我假设你正在构建一个32位程序。如果这是Linux或Mac,我不知道发生了什么!