简单浮点运算的奇怪结果 - FPU内部状态不好?

时间:2009-01-26 17:14:20

标签: c debugging floating-point compiler-flags fpu

我有一个软件项目,我有时会从小而简单的浮点运算中得到奇怪的结果。我假设有一些我错过的东西,并想了解如何调试以下问题的一些提示:

(使用的编译器是MS VC 6.0,即Microsoft C编译器的第12版)

第一个异常:

extern double Time, TimeStamp, TimeStep;  // History terms, updated elsewhere
void timer_evaluation_function( ) {
    if ( ( Time - TimeStamp ) >= TimeStep ) {  
        TimeStamp += TimeStep;  
        timer_controlled_code( );  
    }
{....}

由于某种原因,计时器评估失败,定时代码从未执行。在调试器中,没有问题看到trig条件实际上是真的但FPU拒绝找到正结果。以下代码段虽然执行了相同的操作但没有问题。通过插入可能被允许失败的虚假评估来回避这个问题。

我猜测FPU状态在某种程度上受到先前执行的操作的影响,并且有一些编译器标志可以帮助吗?

第二个异常:

double K, Kp = 1.0, Ti = 0.02;
void timed_code( ){
    K = ( Kp * ( float ) 2000 ) / ( ( float ) 2000 - 2.0F * Ti * 1e6 )
{....}

结果是#IND,即使调试器将等式评估为大约0.05。当使用fld指令将2.0F值加载到FPU时,#INT值出现在FPU堆栈中。上一条指令使用fild指令将整数值2000加载为double float。一旦FPU堆栈包含#IND值,一切都会丢失,但调试器再一次评估公式没有问题。稍后,这些操作将返回预期的结果。

此外,在函数调用之后,FPU问题再次出现。我应该在每个新函数后插入清除FPU状态的浮点运算吗?是否存在可能以某种方式影响FPU的编译器标志?

我很感激此时的所有提示和技巧。

编辑:我已经设法通过在top函数中调用汇编函数EMMS来避免这个问题。这样,FPU就可以清除任何MMX相关的垃圾,这些垃圾可能是在我的代码被调用的环境中创建的,也可能没有。似乎FPU的状态不是理所当然的。

//弗兰克

6 个答案:

答案 0 :(得分:2)

不知道问题是什么,但在x86上,FINIT指令会清除FPU。要测试您的理论,您可以在代码中的某处插入:

__asm {
    finit
}

答案 1 :(得分:1)

这不是你问题的答案,但你可能想看看Raymond Chen关于奇怪的FPU行为的两篇文章。阅读完您的问题并重新阅读文章后,我不会立即看到一个链接 - 但如果您粘贴的代码不完整,或者文章让您了解导致问题的某些周围行为。特别是,如果你在附近的任何地方加载DLL。

Uninitialized floating point variables can be deadly

How did the invalid floating point operand exception get raised when I disabled it?

答案 2 :(得分:1)

如果您在支持MMX的系统上使用Windows QueryPerformanceCounter和QueryPerformanceFrequency函数,请在查询频率/计数器之后和计算之前插入femms指令。

__asm femms

在使用MMX进行64位计算而不清除浮点标志/状态之前,我遇到过这些函数的问题。

如果浮点运算之间存在任何64位运算,也会发生这种情况。

答案 3 :(得分:0)

虽然我没有向您提供确切的解决方案,但我建议您先阅读此article,其中介绍了可以使用的不同优化方法。

答案 4 :(得分:0)

re:timestamps -

您从什么时候获得时间戳来源?有些东西听起来很可疑。尝试将它们记录到文件中。

答案 5 :(得分:0)

如果错误值由应加载2.0的fld加载,我会检查加载此值的内存 - 它可能只是编译器/链接器问题。