День добрый всем. Столкнулся с такой неприятной вещью: есть нетривиальное выражение (выражения) на С, одно из них: Код (Text): erfc_arg = fabs( V - 2.0*n*pl*(1 - pl)) / (2.0*pl*(1 - pl)*sqrt(2*n)); и что имеем в результате - erfc_arg равен 1#IND - то есть, NaN. Происходит это вроде как вследствие переполнения стека FPU - для данного выражения требуются 2 регистра FPU (по крайней мере, компилятор выдал такой код), а их вроде бы как и нет - есть один свободный регистр. Разбил выражение на нескольких простых - все заработало так, как надо. Регистры FPU перед "сбоем" имеют значения: ST0 +1.9990000000000000 e+6 ST1 +1.0000000000000000 e+0 ... ST7 +1.0000000000000000 e+0 TAGS = 0x0000 (!) - т.е. вроде бы как ни одного свободного регистра на стеке. статусный регистр = 0x1261 - есть переполнение. Вопрос в следующем - как вообще бороться с такой "дурью", т.е. есть ли методы кроме как разбиения выражения на более простые? И вообще - глюк ли это компилятора (использую компилер из VS 2003)?
Сорри, приведенное выше значение статусного регистра - конечно, после сбоя. P.S. Да, плохо без редактирования...
Нет, finit - это, конечно, хорошо, но извините - в данном случае речь идет о чистом C, без "засирания" его асм-вставками. Или без этого никак? - в этом-то и вопрос...
bers Но попробовать finit то можно, кроме того в ассемблере применяются команды типа FDIVP, FMULP, FSUBP и т.д. -- выполнение деления, умножения, вычитания и т.д. с последующим выталкиванием из стека FPU -- это также бы препятствовало скорому заполнению st0-st7
bers Да ну прям тут С супер безбажный компилятор, ну забыли они сами вставить команду finit перед начало мат выражений. Я не с одним таким багом уже сталкивался.
bers finit это не решение совсем, точнее решение через ж.... даже если пишешь на чистом асме finit без крайней нужды применять не следует, а уж тем более перед обычным вычислением очередной формулы. Однозначно имеет смысл заглянуть в асемблерный код который слепил С компилятор, чтобы понять чего он там намутил и откуда берётся переполнение стека. Раз finit помог, то скорее всего дело не в этой формуле, а в предшествующих вычислениях и в любом случае нужно анализировать асм код в листинге или отладчике на уровне блока формул - от момента когда он в последний раз был пуст. Если найдёшь настоящий источник переполнения попробуй разбить длинные формулы на несколько более коротких - почти наверняка поможет. ЗЫ: а асм вариант FPU вычислений это не "замусоривание", а как раз очистка полезного кода от компиляторного мусора и глюков )
Короче, после более внимательного просмотра асм-кода дело оказалось в следующем... Обо всем по порядку. В стандартной вижуаловской библиотеке не оказалось весьма нужных функций, именно - erf, erfc, erfcf. По стандартну C99 они все должны быть в библиотеке, поэтому были выдраны из mingw. Файл math.h был поправлен (прототипы), но... не до конца - забыл вставить прототип erf, как самой редкой из вызываемой троицы. Ну а дальше все как по маслу - поскольку собираю C-шным (не плюсами) компилером, получаю следующую картину: компилятор не находит прототипа для erf, поэтому решает, что она возвращает int (на самом деле - double erf(double)). А поскольку сама erf знает лучше, что ей возвращать, то оставляет на стеке по регистру FPU на каждый ее вызов - оберточный код не попит возвращаемое значение (ST0), а берет eax. Отсюда получаем переполнение на 9ый вызов erf. Сорри, ошибка моя. Но думаю, что многим такой печальный опыт может пригодиться...