5

In C# 4.0, the following cast behaves very unexpectedly:

(decimal)1056964.63f
1056965

Casting to double works just fine:

(double)1056964.63f
1056964.625

(decimal)(double)1056964.63f
1056964.625

Is this by design?

mskfisher
  • 3,291
  • 4
  • 35
  • 48
Yuri Astrakhan
  • 8,808
  • 6
  • 63
  • 97
  • Also, it may be worth noting a SQL Server float is not the same as a .NET float. They are not the same. http://stackoverflow.com/questions/122523/why-is-a-sql-float-different-from-a-c-sharp-float – L_7337 Nov 13 '15 at 14:03

2 Answers2

10

The problem is with your initial value - float is only accurate to 7 significant decimal digits anyway:

float f = 1056964.63f;
Console.WriteLine(f); // Prints 1056965

So really the second example is the unexpected one in some ways.

Now the exact value in f is 1056965.625, but that's the value given for all values from about 1056964.563 to 1056964.687 - so even the ".6" part isn't always correct. That's why the docs for System.Single state:

By default, a Single value contains only 7 decimal digits of precision, although a maximum of 9 digits is maintained internally.

The extra information is still preserved when you convert to double, because that's can preserve it without "interpreting" it at all - where converting it to a decimal form (either to print or for the decimal type) goes through code which knows it can't "trust" those last two digits.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Not true. In this case float can actually hold value `1056964.63`, it's the formatting that rounds it to 1056965. Quote from COMNumber::FormatSingle in SSCLI: "In order to give numbers that are both friendly to display and round-trippable, we parse the number using 7 digits and then determine if it round trips to the same value. If it does, we convert that NUMBER to a string, otherwise we reparse using 9 digits and display that." – MagnatLU Nov 27 '11 at 07:53
  • @MagnatLU: It can't actually hold xxx.63. It can hold xxx.625 - but that value is also the closest value to xxx.68, so it's not really *accurate* even to the ".6" part. Read my edited answer and see whether you agree with that more. – Jon Skeet Nov 27 '11 at 07:57
  • I think .NET's `float` is exact implementation of IEEE-754 in which floating point number has exact value determined by it's exponent and fraction and the exact value is used for every calculation (`float` operations are JITted to coprocessor instructions). It's only the conversion to `string` or `COMNumber` when CLI uses custom approach and drops last *signignificant and precise* digits. – MagnatLU Nov 27 '11 at 08:08
  • @MagnatLU: Yes - see my final paragraph. It's when the bits need to be *interpreted* as being significant, and that includes a conversion to `decimal`. – Jon Skeet Nov 27 '11 at 08:14
  • Okay. From the strict floating point rules you can trust those digits, but implementation chooses not to during certain operations. It makes sense for non-sci calculations. I guess your answer is more precise than mine anyway. – MagnatLU Nov 27 '11 at 08:24
-1

It is by design. Float can hold your number [edit]quite accurate[/edit], but for conversion purposes to it rounds it up to nearest integer, because there are only few representable float values between your number and integer (1056964.75 and 1056964.88). See COMNumber::FormatSingle and COMDecimal::InitSingle from SSCLI.

MagnatLU
  • 5,967
  • 1
  • 22
  • 17
  • No, it can't precisely hold the value - otherwise the conversion to `double` would print out 1056964.63 rather than 1056964.625. – Jon Skeet Nov 27 '11 at 08:03
  • You are right. But my point is that the Yurik's results were due to non-IEEE handling during conversion, not due to actual precision loss in representation. – MagnatLU Nov 27 '11 at 08:10