2016-02-03 7 views
4

Я использую Visual C++ для загрузки двоичных данных в float, например:Исключить исключение «Недействительная операция с плавающей точкой»?

double dValue; 
    memcpy(&dValue, lpData, sizeof(dValue)); 

Для нормальных случаев, это будет работать должным образом. Однако в некоторых редких случаях, когда двоичные данные повреждены, dValue будет недействительным, и любые дальнейшие операции над ним вызовут исключение «Недействительная операция с плавающей точкой».

Я проверяю dValue в отладчике и отображается как -1.#QNAN00000000000.

Чтобы предотвратить исключение, мне нужно проверить dValue после его загрузки из двоичных данных. Я пытаюсь использовать:

if (_finite(dValue)) 
    { 
    … do some tasks… 
    } 

Но все-таки инвалид dValue вызовет _finite функции поднять Float-point invalid operation исключения. Хотя _finite(dValue) правильно вернет 0, исключение будет выделено в памяти. Даже если я освобожу его. Слишком много allocate/deallocate все равно исчерпывает системные ресурсы.

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

+0

в стандартном C++ нет способа. Возможно, вы можете проверить спецификацию и код IEEE754 на что-то, что может сработать. –

+1

Если ваш компилятор поддерживает C++ 11, вы должны использовать 'std :: isfinite' от' '. См. [Cppreference.com] (http://en.cppreference.com/w/cpp/numeric/math/isfinite) для получения дополнительной информации. – paddy

+0

@paddy, который все еще не решит проблему –

ответ

1

Я бы ожидал, что std::isnan сделает все, что вам нужно, и сделает это наиболее эффективно. Если это не так для вашей реализации на вашей платформе, но вы чувствуете себя комфортно, полагая, что числа с плавающей запятой используют формат IEEE   754, нетрудно написать эти функции самостоятельно.

inline constexpr bool 
isnan(const std::uint32_t bits) noexcept 
{ 
    constexpr std::uint32_t exponent = UINT32_C(0x7f800000); 
    constexpr std::uint32_t mantissa = UINT32_C(0x007fffff); 
    return ((bits & exponent) == exponent) && ((bits & mantissa) != 0); 
} 

inline constexpr bool 
isnan(const std::uint64_t bits) noexcept 
{ 
    constexpr std::uint64_t exponent = UINT64_C(0x7ff0000000000000); 
    constexpr std::uint64_t mantissa = UINT64_C(0x000fffffffffffff); 
    return ((bits & exponent) == exponent) && ((bits & mantissa) != 0); 
} 

Реализация isfinite еще проще, у вас есть только проверить, что (bits & exponent) != exponent. Чтобы реализовать isnormal, отметьте, что ((bits & exponent) != exponent) && ((bits & exponent) != 0)). Обратите внимание, что ноль не является «нормальным» по этому определению.

Обратите внимание, что функции работают с интегральными типами. Это намеренно. Ваш код будет fread байтов в целое число, вы передаете целое число в функции, чтобы определить, представляет ли он «не число» и (только), если это не так, вы memcpy байты в тип с плавающей запятой.

+0

Другим способом проверки для NaN с использованием типов с плавающей точкой IEEE является 'a! = A'. Это выглядит глупо, но значения NaN ** никогда не сравниваются. Однако возможно, что слишком умный компилятор оптимизирует тест, который может иметь только один результат; в этом случае может потребоваться немного коснуться, чтобы подделать оптимизатор. –

+0

@PeteBecker Идея состояла в том, чтобы проверить для NaN * перед * преобразование бит в тип с плавающей точкой, чтобы избежать накладных расходов FPU, с которыми сталкивался OP. – 5gon12eder

+0

Yup, я забыл это в вашем ответе. Тем не менее ...