странные результаты простых операций с плавающей точкой - плохое внутреннее состояние FPU? - PullRequest
4 голосов
/ 26 января 2009

У меня есть программный проект, в котором я иногда получаю странные результаты от небольших, простых операций с плавающей запятой. Я предполагаю, что что-то упустил, и хотел бы дать несколько советов о том, как отладить следующие проблемы:

(используется компилятор MS VC 6.0, то есть версия 12 компилятора Microsoft C)

Первая аномалия:

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

По какой-то причине оценка таймера завершилась неудачно, а таймерный код никогда не выполнялся. В отладчике не было проблем с тем, чтобы убедиться, что условие триггера действительно выполнялось, но 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. Значение #IND появляется в стеке FPU, когда значение 2.0F загружается в FPU из-за использования инструкции fld. Предыдущая инструкция загружает целочисленное значение 2000 как двойное число с использованием инструкции fild. Как только стек FPU содержит значение #IND, все теряется, но у отладчика снова возникает проблема с вычислением формулы. Позже эти операции возвращают ожидаемые результаты.

Кроме того, снова проблемы с FPU возникают непосредственно после вызова функции. Должен ли я вставлять операции с плавающей запятой, которые очищают состояние FPU после каждой новой функции? Есть ли флаг компилятора, который может как-то повлиять на FPU?

Я благодарен за все советы и рекомендации на данный момент.

РЕДАКТИРОВАТЬ: мне удалось избежать проблемы, вызвав функцию сборки EMMS первым делом в верхней функции. Таким образом, FPU очищается от любого связанного с MMX мусора, который мог или не мог быть создан в среде, из которой вызывается мой код. Похоже, что состояние FPU не является чем-то само собой разумеющимся.

// Франк

Ответы [ 6 ]

2 голосов
/ 26 января 2009

Понятия не имею, в чем может быть проблема, но на x86 инструкции FINIT очищают FPU. Чтобы проверить свою теорию, вы можете вставить это где-нибудь в свой код:

__asm {
    finit
}
1 голос
/ 27 января 2009

Если вы используете функции Windows QueryPerformanceCounter и QueryPerformanceFrequency в системе, поддерживающей MMX, попробуйте вставить инструкцию femms после запроса частоты / счетчика и перед вычислением.

__asm femms

Я сталкивался с проблемами этих функций раньше, когда они выполняли 64-битные вычисления с использованием MMX и не очищали флаги / состояния с плавающей запятой.

Такая ситуация также может возникнуть, если между операциями с плавающей запятой существует 64-битная арифметика.

1 голос
/ 27 января 2009

Это не совсем ответ на ваш вопрос, но вы можете посмотреть две статьи Раймонда Чена о странном поведении FPU. Прочитав ваш вопрос и перечитав статьи, я не сразу увижу ссылку - но если код, который вы вставили, не завершен, или если статьи дают вам представление о некотором окружающем поведении, которое вызвало проблему ... в частности, если вы загружаете DLL где-нибудь поблизости.

Неинициализированные переменные с плавающей запятой могут быть смертельными

Как возникло недопустимое исключение операнда с плавающей запятой, когда я его отключил?

0 голосов
/ 27 января 2009

Если неверное значение загружается полем, которое должно загружать 2.0, я бы проверил память, из которой загружено это значение - это может быть просто проблема компилятора / компоновщика.

0 голосов
/ 26 января 2009

re: метки времени -

Откуда вы берете свой источник меток времени? Что-то звучит подозрительно. Попробуйте записать их в файл.

0 голосов
/ 26 января 2009

Хотя я не предоставляю вам точного решения, я предлагаю вам начать с прочтения этой статьи , которая описывает различные оптимизации, которые можно использовать.

...