Недопустимая операция с плавающей запятой при вводе float в строковый поток

У меня есть простой фрагмент кода, который извлекает число с плавающей точкой из сгенерированного FORTRAN массива REAL, а затем вставляет его в поток для регистрации. Хотя это работает для первых 30 случаев, 31-го происходит сбой с «Недопустимой операцией с плавающей запятой».

Код:

int FunctionDeclaration(float* mrSwap)
{
...
float swap_float;
stringstream message_stream;
...
swap_float = *(mrSwap+30-1);
...
message_stream.clear();
message_stream <<  30 << "\t" << swap_float << "\tblah blah blah \t";

При отладке значение swap_float экземпляра перед сбоем (в последней строке выше) равно 1711696,3, кроме того, что оно намного больше, чем большинство значений до этого момента, в этом нет ничего особенного.

Я также попытался заменить message_stream на cerr и получил ту же проблему. До сих пор я считал, что cerr практически неразрушим — как простой поплавок может его уничтожить?

Изменить:

Спасибо за комментарии: я добавил объявление mrSwap. mrSwap имеет длину около 200, так что я далеко от конца. Он заполняется вне моего контроля, и отдельные записи могут не заполняться, но, насколько я понимаю, это просто означает, что swap_float будет установлен на случайное число с плавающей запятой?


person Mike Sadler    schedule 09.05.2012    source источник
comment
Можете ли вы сделать это небольшим компилируемым примером? Что такое mrSwap и как он заполняется?   -  person hmjd    schedule 09.05.2012
comment
Похоже, вы преодолели конец.   -  person Flexo    schedule 09.05.2012
comment
Если это операция с указателем, мне было бы интересно, если бы вы только что прошли конец массива. С cerr проблем нет, я уверен.   -  person duffymo    schedule 09.05.2012
comment
Вы ссылаетесь на mrSwap, но не показываете, что это такое.   -  person Sebastian Mach    schedule 09.05.2012
comment
Вы пробовали использовать printf() вместо cout?   -  person zvrba    schedule 09.05.2012
comment
Думаю, это мой следующий шаг - по крайней мере, после обеда. По крайней мере, printf/sprintf объявляют типы, которые они ожидают, поэтому могут давать более содержательные сообщения об ошибках...   -  person Mike Sadler    schedule 09.05.2012
comment
@MikeSadler Попытка прочитать значение с плавающей запятой, которое не было инициализировано, является неопределенным поведением. Ваш случайный поплавок на самом деле может быть представлением захвата. (Согласно стандарту, это верно для любого несимвольного типа. На практике, однако, я знаю только одну машину, на которой может быть проблема с int, тогда как на некоторых наиболее распространенных проблемах с float платформы: Intel, Sparc...)   -  person James Kanze    schedule 09.05.2012
comment
По словам моего коллеги, это конкретное значение не должно быть неопределенным, но поскольку этот конкретный фрагмент кода предназначен для двойной проверки содержимого массива, я хотел бы, чтобы он справлялся (т.е. предпочтительно распознавал ) неопределенные ячейки.   -  person Mike Sadler    schedule 09.05.2012


Ответы (2)


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

Категорически нет. Определенные битовые шаблоны в числе с плавающей запятой IEEE указывают на недопустимое число — например, результат переполнения арифметической операции или недопустимое число (например, 0,0/0,0). Загадочная вещь здесь заключается в том, что отладчик, по-видимому, принимает число как допустимое, а cout — нет.

Попробуйте получить битовую раскладку swap_float. В 32-битной системе:

int i = *(int*)&swap_float;

Затем напечатайте i в шестнадцатеричном формате и сообщите нам, что вы видите.

Добавлено: Из комментария Майка i=1238430338, что соответствует 49D0F282 в шестнадцатеричном формате. Это действительное число с плавающей запятой, точно равное 1711696,25. Так что я не знаю, что происходит, я боюсь. Единственное, что я могу предположить, это то, что, возможно, компилятор загружает недопустимое число с плавающей запятой непосредственно из массива mrSwap в банк регистров с плавающей запятой, минуя swapFloat. Так что истинное значение swapFloat просто недоступно отладчику. Чтобы проверить это, попробуйте

int j = *(int*)(mrSwap+30-1);

и расскажи нам, что ты видишь.

Обновлено снова, чтобы добавить: Другая возможность — отложенная ловушка с плавающей запятой. Сопроцессор с плавающей запятой (в наши дни встроенный в ЦП) генерирует прерывание с плавающей запятой из-за какой-то недопустимой операции, но прерывание не замечается до тех пор, пока не будет выполнена следующая операция с плавающей запятой. пытался. Таким образом, этот сбой может быть результатом предыдущей операции с плавающей запятой, которая может быть где угодно. Удачи с этим...

person TonyK    schedule 09.05.2012
comment
Спасибо Тони - стоит знать о поплавках. Когда я делаю, как вы предлагаете, i=1238430338 в отладчике и при отправке в cerr. Попробую распечатать в шестнадцатеричном формате... - person Mike Sadler; 09.05.2012
comment
Хорошо, в шестнадцатеричном формате i=49d0f282. Я также пытался использовать printf(%f, swap_float), и это выдает точно такую ​​же ошибку. - person Mike Sadler; 09.05.2012
comment
После первого редактирования j=49d0f282 - так же, как и раньше. При втором редактировании: это звучит неприятно правдоподобно, поскольку отладчик, похоже, оказывается в «запутанном» состоянии — вторая запись в стеке: [Кадры ниже могут быть неверными и/или отсутствовать, символы для ntdll.dll не загружены] . К сожалению, у меня нет опыта использования отладчиков, поэтому я не могу понять все подсказки... - person Mike Sadler; 09.05.2012
comment
Однако я знаю, какой была предыдущая операция с плавающей запятой. Последний набор '...' в моем фрагменте на самом деле является вызовом этой функции: bool IsSame(float swap_value, double function_value, двойной допуск) { return (abs(swap_value - (float)function_value) ‹ допуска); } Компилятор предупреждает меня о потере точности, но, похоже, работает нормально и возвращает правильный результат, так что я не удосужился упомянуть об этом. - person Mike Sadler; 09.05.2012
comment
ПОНЯТНО! Вы были абсолютно правы, TonyK - в моем сравнении с использованием IsSame другим значением было NaN (это допустимое значение в данном контексте), и хотя он с радостью вычел его из swap_float, он поставил флаг, говорящий сообщить о следующая операция как ошибка. Должен сказать, что я совершенно не знал, что это возможно — я думал, что если это сработает, то сработает. - person Mike Sadler; 09.05.2012

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

"Еще одна возможность - отложенная ловушка с плавающей запятой. Сопроцессор с плавающей запятой (в наши дни встроенный в ЦП) генерирует прерывание с плавающей запятой из-за какой-то недопустимой операции, но прерывание не замечается до тех пор, пока предпринята попытка следующей операции с плавающей запятой. Таким образом, этот сбой может быть результатом предыдущей операции с плавающей запятой, которая может быть где угодно." — TonyK

Это действительно было проблемой: в моем сравнении с использованием IsSame другим значением было NaN (это действительное значение в данном контексте), и хотя оно было успешно вычтено из swap_float, оно поставило флаг, сообщая о следующей операции как о следующей операции. ошибка. Должен сказать, что я совершенно не знал, что это возможно — я думал, что если это сработает, то сработает.

person Mike Sadler    schedule 02.09.2016