2011-01-14 3 views
0

Я пытаюсь преобразовать double в float, а также различные целочисленные типы внутри dll, который используется в качестве расширения Game Maker. Мне не нужен разумный результат, если double не соответствует диапазону целевых типов, поэтому я просто использовал static_cast.Как предотвратить/пресекать SIGFPE на C++?

Все работает по назначению, когда я вызываю этот код из своего собственного тестового приложения на C++, но когда он вызывается из Game Maker, ошибки диапазона поднимают SIGFPE по какой-то причине, что приводит Game Maker к завершению моей программы с сообщением об ошибке.

Мне не нужны разумные результаты для конверсий вне диапазона, но сбой - это не-нет. Я попробовал использовать llround вместо cast, но также поднимает сигнал.

Я также попытался поймать сигнал сам, используя сигнал (SIGFPE, SIG_IGN); прямо перед конверсией, но он вообще не менял поведения. Может быть, зловещий комментарий в mingw signal.h имеет какое-то отношение к этому: «SIGFPE, похоже, не работает?»

Я проверил исходный код другой dll, используемой в расширении Game Maker, и предоставленный автором двоичный файл выполняет простые преобразования конверсий без проблем. Однако, когда я компилирую исходный код, проблема SIGFPE присутствует снова. Я предполагаю, что автор использовал другой компилятор, но я предпочел бы остаться с mingw, если это возможно.

Итак, как я могу выполнить эти преобразования безопасно или предотвратить генерирование сигнала, когда я выполняю их с помощью простого трансляции? Я использую mingw-g ++ 4.5.0 для компиляции на данный момент.

Вот функция, где проблема происходит:

template<typename ValueType> 
static double writeIntValue(double handle, double value) { 
    boost::shared_ptr<Writable> writable = handles.find<Writable>(handle); 
    if(writable) { 
     // Execution reaches this point 
     ValueType converted = static_cast<ValueType>(value); 
     // Execution doesn't reach this point if e.g. ValueType 
     // is short and value is 40000 
     writable->write(reinterpret_cast<uint8_t *>(&converted), sizeof(converted)); 
    } 
    return 0; 
} 
+0

Вы уверены, что это переходы вне диапазона, которые вызывают SIGFPE, а не деление на 0? –

+0

Да. Я добавил код функции, в которой возникает проблема, и нет ни деления, ни 0. – Medo42

ответ

1

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

ValueType converted; 
if(value >= std::numeric_limits<ValueType>::max()) { 
    converted = std::numeric_limits<ValueType>::max(); 
} else if(value <= std::numeric_limits<ValueType>::min()) { 
    converted = std::numeric_limits<ValueType>::min(); 
} else { 
    converted = static_cast<ValueType>(value); 
} 

Другой вариант заключается в использовании numeric_cast из библиотек Boost, которая бросает исключение, если значение источника находится вне диапазона, поэтому он определил поведение для всех преобразования.

Документация библиотеки ускорения с числовым преобразованием содержит helpful information о том, как в стандарте определены определенные преобразования.

Благодарим за предоставление правильного предложения в его ответе, но, к сожалению, его примерный код испорчен, и я хотел добавить дополнительные указатели, которые мне помогли.

0

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

+0

Код добавлен. Я узнал, что SIGFPE поднимается в этом месте с помощью отладчика, и если он был подключен к недопустимой памяти, проблема не произойдет точно, когда значение выходит за пределы диапазона. – Medo42

1

Поскольку вы используете DLL, вы уверены, что DLL скомпилирована так же, как программа ожидает этого? Может быть, несоответствие 32/64 бит?

Кроме того, SIGFPE также может быть поднят при переполнении при переполнении.

Вы можете включить/отключить сигнал, поднятый этим переполнением, установив маску с помощью _FPU_SETCW (это находится в fpu_control.h). Я предполагаю, что Game Maker не позволяет это и вашу тестовую программу.

Я никогда не пробовал это, и я не уверен, что mingw также имеет это, но я надеюсь, что это немного поможет.

редактировать:

Почему не убедившись, что переполнение не происходит?

что-то вроде:

if (value > std::numeric_limits<ValueType>::max()) 
{ 
    value = std::numeric_limits<ValueType>::max(); 
} 
else if (value < std::numeric_limits<ValueType>::min()) 
{ 
    value = std::numeric_limits<ValueType>::min(); 
} 
ValueType converted = value; 
+0

Спасибо за ваш ответ. fpu_control.h недоступен, поэтому я не мог его протестировать, но зажав значение в целевом диапазоне, так как это решает мою проблему. Я бы отметил это как решение, но осталась небольшая проблема: преобразование в int64_t все равно может быть неудачным, потому что сохранение его max в double может быть неточным и фактически снова дает вам значение за пределами диапазона int64_t. Это легко решить, конечно, но я хочу, чтобы это было правильно в ответ для следующего человека, который ищет это :) – Medo42

Смежные вопросы