2

I have the following code to test the conversion from double to long:

double dVal = (double)(long.MaxValue);  // 9.2233720368547758E+18;
if (dVal <= long.MaxValue && dVal >= long.MinValue)
{
  long lVal1 = (long)(dVal);           // gives -9223372036854775808 !!!
  Console.WriteLine($"lVal1 = {lVal1}");
  long lVal2 = Convert.ToInt64(dVal);  // gives System.OverflowException
}

I expected lVal1/2 to be 9223372036854775807 or 9223372036854775800 because of the truncation or rounding in the double type.

How can I convert double to long correctly for all cases? Is there something in the framework already or do I need to implement it?

schoetbi
  • 12,009
  • 10
  • 54
  • 72
  • It's just not possible, because information is lost when converting your long to double. Try this: `double dVal = long.MaxValue; double dVal2 = long.MaxValue - 1; Console.WriteLine(dVal == dVal2); // true` – Evk Nov 23 '17 at 09:45
  • @Evk: I try to do the reverse. Not doubleValue = longValue but longValue=doubleValue – schoetbi Nov 23 '17 at 09:49
  • You can get rid of the rounding error by storing the floating point number as a long and keep track of the exponent 18. long dVal = 92233720368547758 – jdweng Nov 23 '17 at 09:50
  • 1
    First line of your code is `double dVal = (double)(long.MaxValue);`. At this point some information is lost and cannot be restored. Both `long.MaxValue` and `long.MaxValue - 1` (and other close values) convert to exactly the same double. That means you cannot get your long value back from double, because your `dVal` corresponds to a range of long values, not to exactly 1. – Evk Nov 23 '17 at 09:50

2 Answers2

1

As @HimBromBeere states this is a rounding error. Both long and double datatypes use 64-bits (8 bytes) to represent their number. But as int is an integral type, it stores integer values precisely between their minimum and maximum values and nothing outside of that. double however is a floating point number which has a much larger range (both in terms of min/max values but also sub-integer values including very small values. This comes at a cost of precision so double won't be able to differentiate long.MaxValue and long.MaxValue-1 for example in the same way that long won't be able to differentiate 0.1 and 0.2.

Chris Walsh
  • 3,423
  • 2
  • 42
  • 62
1

Why do you expect what you expect? The C# docs are pretty clear about what happens in these two cases:

Case 1: Casting from double using an explicit cast (from here)

When you convert from a double or float value to an integral type, the value is truncated. If the resulting integral value is outside the range of the destination value, the result depends on the overflow checking context. In a checked context, an OverflowException is thrown, while in an unchecked context, the result is an unspecified value of the destination type.

Case 2: Calling Convert.ToInt64 (from here)

Exceptions

OverflowException

value is greater than MaxValue or less than MinValue.

The answer to your question depends on what you want it to do in these cases? The reason you have to do an explicit cast is because there is a possibility to lose information (since doubles can store values that don't fit into a long). You have to tell the compiler what you want to do in these edge cases.

Kolichikov
  • 2,944
  • 31
  • 46
  • 1
    "When you convert from a double or float value to an integral type" OP is doing the other way round, as mentioned in your headline also: "Case 1: Casting to double". But the quote from MSDN doesn´t fit this headline. – MakePeaceGreatAgain Nov 23 '17 at 09:45
  • @HimBromBeere you're right. I meant casting from double to long, since that's what's happening in the question. – Kolichikov Nov 23 '17 at 09:48