2

What is the maximum double value that can be represented\converted to a decimal?

How can this value be derived - example please.

Update

Given a maximum value for a double that can be converted to a decimal, I would expect to be able to round-trip the double to a decimal, and then back again. However, given a figure such as (2^52)-1 as in @Jirka's answer, this does not work. For example:

    Test]
    public void round_trip_double_to_decimal()
    {
        double maxDecimalAsDouble = (Math.Pow(2, 52) - 1);

        decimal toDecimal = Convert.ToDecimal(maxDecimalAsDouble);

        double toDouble = Convert.ToDouble(toDecimal);

        //Fails.
        Assert.That(toDouble, Is.EqualTo(maxDecimalAsDouble));
    }
Tim Lloyd
  • 37,954
  • 10
  • 100
  • 130
  • I am not sure if there could be any max value as such coz Some floating point numbers cannot be accurately represented by double and there is a loss of precision which need not be at higher levels as such – V4Vendetta May 28 '12 at 11:35

2 Answers2

4

All integers between -9,007,199,254,740,992 and 9,007,199,254,740,991 can be exactly represented in a double. (Keep reading, though.)

The upper bound is derived as 2^53 - 1. The internal representation of it is something like (0x1.fffffffffffff * 2^52) if you pardon my hexadecimal syntax.

Outside of this range, many integers can be still exactly represented if they are a multiple of a power of two.

The highest integer whatsoever that can be accurately represented would therefore be 9,007,199,254,740,991 * (2 ^ 1023), which is even higher than Decimal.MaxValue but this is a pretty meaningless fact, given that the value does not bother to change, for example, when you subtract 1 in double arithmetic.

Based on the comments and further research, I am adding info on .NET and Mono implementations of C# that relativizes most conclusions you and I might want to make.

  • Math.Pow does not seem to guarantee any particular accuracy and it seems to deliver a bit or two fewer than what a double can represent. This is not too surprising with a floating point function. The Intel floating point hardware does not have an instruction for exponentiation and I expect that the computation involves logarithm and multiplication instructions, where intermediate results lose some precision. One would use BigInteger.Pow if integral accuracy was desired.

  • However, even (decimal)(double)9007199254740991M results in a round trip violation. This time it is, however, a known bug, a direct violation of Section 6.2.1 of the C# spec. Interestingly I see the same bug even in Mono 2.8. (The referenced source shows that this conversion bug can hit even with much lower values.)

  • Double literals are less rounded, but still a little: 9007199254740991D prints out as 9007199254740990D. This is an artifact of internal multiplication by 10 when parsing the string literal (before the upper and lower bound converge to the same double value based on the "first zero after the decimal point"). This again violates the C# spec, this time Section 9.4.4.3.

  • Unlike C, C# has no hexadecimal floating point literals, so we cannot avoid that multiplication by 10 by any other syntax, except perhaps by going through Decimal or BigInteger, if these only provided accurate conversion operators. I have not tested BigInteger.

  • The above could almost make you wonder whether C# does not invent its own unique floating point format with reduced precision. No, Section 11.1.6 references 64bit IEC 60559 representation. So the above are indeed bugs.

So, to conclude, you should be able to fit even 9007199254740991M in a double precisely, but it's quite a challenge to get the value in place!

The moral of the story is that the traditional belief that "Arithmetic should be barely more precise than the data and the desired result" is wrong, as this famous article demonstrates (page 36), albeit in the context of a different programming language.

Don't store integers in floating point variables unless you have to.

Community
  • 1
  • 1
Jirka Hanika
  • 13,301
  • 3
  • 46
  • 75
  • You're probably right: the OP did very likely mean "exactly represented" without saying it in his question. – Sergey Kalinichenko May 28 '12 at 11:45
  • @Jirka So with the upper bound of "2^52-1" would you expect that I could convert doubles to decimals, and then back to doubles within that range and the resulting double would be equivalent to the input double i.e. the doubles would round-trip? – Tim Lloyd May 28 '12 at 12:11
  • 1
    @Jirka I am not able to rountrip (2^52)-1. Please see my update. – Tim Lloyd May 28 '12 at 13:15
  • @chibacity - Your calculation goes through 2^52 which is outside the range, why don't you simply use `4503599627370495M` ? Hm, it's not the whole story, I'm researching. – Jirka Hanika May 28 '12 at 13:53
  • @Jirka I am using 4503599627370495M i.e. (2^52)-1. – Tim Lloyd May 28 '12 at 14:09
  • @chibacity, my expectation was wrong. I was putting too much trust in .NET and mono implementing the ECMA 334 standard literally. See the edited answer for details. – Jirka Hanika May 28 '12 at 16:23
  • @Jirka Very interesting stuff. The extended answer seems to be more answering the question of what is the biggest integer that can be stored in a double. To keep on the matter of integers, I'm really looking for the largest integer than can be stored as a double *and* then converted to a decimal. – Tim Lloyd May 28 '12 at 19:30
  • @chibacity - If a runtime is buggy, each version may be differently buggy. In my experimentation on mono 2.8, apart from the "multiplication by ten effect", which makes for the loss of one decimal digit, the rest seems to work. I referenced a source indicating that .NET 4 beta might have been much worse. Here is roughly how I tested: `Console.WriteLine("{0:N}", (decimal)(double)(decimal)(double)900719925474099M); Console.WriteLine("{0:N}", (decimal)(double)(decimal)(double)900719925474100M);` That is, 2^52 divided by 10, or slightly more. – Jirka Hanika May 28 '12 at 19:49
0

MSDN Double data type

Decimal vs double

The value of Decimal.MaxValue is positive 79,228,162,514,264,337,593,543,950,335.

Community
  • 1
  • 1
lex87
  • 1,276
  • 1
  • 15
  • 33