странные результаты простых операций с плавающей запятой — плохое внутреннее состояние FPU?

StackOverflow https://stackoverflow.com/questions/480562

Вопрос

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

(используется компилятор 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, который мог или не мог быть создан в среде, из которой вызывается мой код.Похоже, состояние ППУ нельзя воспринимать как нечто само собой разумеющееся.

//Откровенный

Это было полезно?

Решение

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

__asm femms

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

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

Другие советы

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

__asm {
    finit
}

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

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

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top