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.
Important Update: if you are referring to this question, you should also read this question: IEEE-754 Double (64-bit floating point) vs. Long (64-bit Integer) Revisited
It notes a flaw in the answer where some very large values are also able to be exactly represented by IEEE-754. While this may mean that the value will correctly round-trip, for my original purpose (will it round-trip to JavaScript) it will not.
Also there appears to be a bug in the CLRs System.Double type because it doesn't correctly allow these values to round-trip.
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).
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.
IEEE 754 Double has 52 bits for mantissa and you are converting from/to integer/long so it quite easy to test. If your integer 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 simply if all bits higher than the 52th bit is one like ((aULong && 0xFFF0000000000000) == 0).
For long, you have to bring 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.
来源:https://stackoverflow.com/questions/1601646/how-to-test-if-numeric-conversion-will-change-value