tags:

views:

120

answers:

3

I'm performing some data type conversions where I need to represent uint, long, ulong and decimal as IEEE 754 double floating point values. I want to be able to detect if the IEEE 754 data type cannot contain the value before I perform the conversion.

A brute force solution would be to wrap a try-catch around a cast to double looking for OverflowException. Reading through certain of the CLR documentation implies that some conversions just silently change the value without any exceptions.

Is there any fool proof way to do this check? I'm looking for completeness over ease of implementation. I have a feeling I'm going to be closely reading the IEEE 754 spec and checking matissa and exponent carefully...

I should add, that I am most concerned with representing whole numbers accurately and that loss of floating point precision is of secondary concern (but still worth considering).

EDIT: Int32 is able to be fully expressed as IEE-754. Also the Decimal data type is very much part of the question.

+8  A: 

Simple solution could be something like (if x is an int):

if ((int)(double)x != x) { 
  // won't convert
} else {
  // will convert
}

and so on for long, etc.

(double)x will convert x from an int to a double. The (int) then converts it back again. So (int)(double)x converts form an int to a double and then back. Essentially the code is checking that the conversion to double is reversible (and therefore that the double can store the exact value of the int).

atomice
I'm not sure why this was downvoted - it seems the simplest and most obviously correct solution to me.
Jon Skeet
+1 - It is a simple answer, you may want to put the "if x is an int" clarification in bold, as it will easily be missed.
James Black
You may want to explain how (int)(double) would work, as this is not a common casting, and except for the comment from Jon Skeet, it may be assumed that it won't work.
James Black
The int `x` is first casted (converted, in fact) to a double, then back to an int. If the roundtrip is successful - that is `x` equals the roundtripped `x` - it is obviously safe to store this number into the double.
Lucero
Wouldn't int be always within the available IEEE-754 space?So expanding upon this answer, could I create a helper method that covers all cases (long, uint, ulong, decimal) like this? bool InvalidIeee754(decimal value) { try { return !Decimal.Equals((double)value, value); } catch { return true; } }
McKAMEY
@McKAMEY: int and uint will always fit (just 32 bits), yes. However, 64/63-bit numbers (ulong, long) and decimals (128 bit floating point) will not.
Lucero
Yes I guess that would work. I would not use decimal at all though. Just have several overloaded InvalidIeee754 methods, one that takes a long argument, one that takes an int, etc. Then you don't have an unnecessary conversion to decimal.
atomice
In my code snippet there, it appears that Decimal.Equals retains too much information about original precision. Unless anyone can think of why this wouldn't work, I'm leaning towards an expanded version of atomice's answer: bool InvalidIeee754(decimal value) { try { return (decimal)((double)value) != value; } catch { return true; } }
McKAMEY
True about the extra decimal conversion. I'll have to weigh the D.R.Y. aspects of one method verses the performance difference.
McKAMEY
+1  A: 

This mainly depends on the number range you're operating with. As long as you're within 15 digits (for a double), you should be on the safe side for whole numbers.

Basically, what you need to take into account is the number of significant digits. So as long as you number is smaller than the significant digit limit, it will remain exact; if it gets larger, you'll lose precision (even if those are whole numbers).

So as long as your number is < 2^53, you're usually good.

Lucero
+1  A: 

IEEE 754 Double has 52 bits for mantissa and you are converting from/to integer/long so it quite easy to test. If your interger consume less than 52 bits then it should be convertible without problem to IEEE 754 double.

I assume (I know for sure in case of Java but not C# and lazy to check) that int is 32 bits and long is 64 bits. So for sure int can fit in double without any problem both sign and unsign.

For ulong, you simpley if all bits higher than the 52th bit is one like ((aULong && 0xFFF0000000000000) == 0).

For long, you have to bing its sign in to the consideration. Since Long is 2nd-complement but IEEE754 is not (just have negative bit), it think it is safe to just covert negative long to positive (*-1) and check like positive. So, if the long is negative, time it with -1 first (do nothing for positive). Then, Check it like ulong.

Hope this helps.

NawaMan