4

I am using Visual C++ to load binary data into float, like this:

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

For normal cases, this will work properly. However, for some rare cases, when binary data are corrupt, the dValue will be invalid and any further operations on it will cause “Float-point invalid operation” exception.

I check the dValue in debugger and it displays as -1.#QNAN00000000000.

To prevent the exception, I need to verify the dValue after it is loaded from the binary data. I try to use:

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

But still the invalid dValue will cause _finite function to raise a Float-point invalid operation exception. Although _finite(dValue) will return 0 properly, the exception will be allocated in memory. Even if I deallocate it. Too many allocate/deallocate will still exhaust the system resources.

Therefore, is there a way to detect the validity of a double value without raising any exceptions?

phuclv
  • 37,963
  • 15
  • 156
  • 475
alancc
  • 487
  • 2
  • 24
  • 68
  • in Standard C++ there is no way. Perhaps you could check the IEEE754 spec and code up something , that's likely to work. – M.M Feb 03 '16 at 03:17
  • 1
    If your compiler supports C++11, you should be using `std::isfinite` from ``. See [cppreference.com](http://en.cppreference.com/w/cpp/numeric/math/isfinite) for more info. – paddy Feb 03 '16 at 03:20
  • @paddy that still wouldn't solve the problem – M.M Feb 03 '16 at 03:21
  • 1
    This is just an argument against using binary data formats. It's much easier to determine validity of lightweight-parsable text-based file formats, like xml or yaml. – Sam Varshavchik Feb 03 '16 at 03:21
  • Could be a sledge-hammer to crack a nut, but there is this: https://blogs.msdn.microsoft.com/dougste/2008/11/12/random-and-unexpected-exception_flt_divide_by_zero-and-exception_flt_invalid__operation/ -- I agree with the suggestion to use text representation. Binary requires manual validation, and is not even portable, since the IEEE754 does not specify endianness. – paddy Feb 03 '16 at 03:32
  • 1
    `_finite()` is just checking for _valid_ finite values. Your problem is harder than this: a bit pattern that's not a valid number for the fpu, which (I believe) is invoking a trap handler. You can catch such fpu traps. See https://msdn.microsoft.com/en-us/library/te2k2f2t.aspx. Also, https://msdn.microsoft.com/en-us/library/c9676k6h.aspx for setting up fpu modes including handler invocation. – Gene Feb 03 '16 at 03:38
  • http://stackoverflow.com/a/14582105/995714 – phuclv Feb 03 '16 at 04:35
  • @Gene what are invalid bit values? I know NAN and INF but they are valid bit patterns – phuclv Feb 03 '16 at 04:39
  • I'm not an IEEE fp expert, but believe e.g. that denormalized numbers cause exceptions depending on how the control word is set. – Gene Feb 03 '16 at 04:51
  • Does simply trying the operation and then catching and handling the floating point exception (if it occurs) count as a way to detect validity? – Jeremy Friesner Feb 03 '16 at 05:04

1 Answers1

1

I would have expected that std::isnan would do what you need and do it most efficiently. If that is not the case for your implementation on your platform, but you feel comfortable assuming that floating point numbers use IEEE 754 format, it is not hard to write those functions yourself.

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);
}

Implementing isfinite is even simpler, you only have to check that (bits & exponent) != exponent. To implement isnormal, check that ((bits & exponent) != exponent) && ((bits & exponent) != 0)). Note that zero is not “normal” by this definition.

Note that the functions operate on integral types. This is intentional. Your code would fread the bytes into an integer, you pass the integer to the functions to determine whether it represents “not a number” and (only) if it doesn't, you memcpy the bytes into a floating-point type.

5gon12eder
  • 24,280
  • 5
  • 45
  • 92
  • Another way to test for NaN with IEEE floating-point types is `a != a`. It looks silly, but NaN values **never** compare equal. However, it's possible that an overly smart compiler would optimize out a test that can only have one result; in that case it might be necessary to do a bit of indirection to fake out the optimizer. – Pete Becker Feb 03 '16 at 14:54
  • @PeteBecker The idea was to test for NaNs *before* converting the bits to a floating-point type so to avoid the FPU overhead the OP was facing. – 5gon12eder Feb 03 '16 at 20:04
  • Yup, I overlooked that in your answer. Nevertheless... – Pete Becker Feb 03 '16 at 20:09