I tried the equivalent of Michael Meadows EDIT 2, but in VB.NET and got a different result. (Specifically both the Double
and Decimal
results were 600000.0238418580.)
I have determined the difference is with the compile-time accuracy of a float
(Single
) division stored into a float
in C#, (which seems to be more equivalent to VB.NET's accuracy when storing into a Double
) and what happens (in both languages unsurprisingly) when you force the division to occur at runtime.
So, THREE_FIFTHS
and vTHREE_FIFTHS
provide different results for the asDouble
summation:
const int ONE_MILLION = 1000000;
float THREEsng = 3f;
float FIVEsng = 5f;
float vTHREE_FIFTHS = THREEsng / FIVEsng;
const float THREE_FIFTHS = 3f / 5f;
Console.WriteLine("Three Fifths: {0}", THREE_FIFTHS.ToString("F10"));
float asSingle = 0f;
double asDouble = 0d;
decimal asDecimal = 0M;
for (int i = 0; i < ONE_MILLION; i++)
{
asSingle += (float) THREE_FIFTHS;
asDouble += (double) THREE_FIFTHS;
asDecimal += (decimal) THREE_FIFTHS;
}
Console.WriteLine("Six Hundred Thousand: {0:F10}", THREE_FIFTHS * ONE_MILLION);
Console.WriteLine("Single: {0}", asSingle.ToString("F10"));
Console.WriteLine("Double: {0}", asDouble.ToString("F10"));
Console.WriteLine("Decimal: {0}", asDecimal.ToString("F10"));
Console.WriteLine("vThree Fifths: {0}", vTHREE_FIFTHS.ToString("F10"));
asSingle = 0f;
asDouble = 0d;
asDecimal = 0M;
for (int i = 0; i < ONE_MILLION; i++)
{
asSingle += (float) vTHREE_FIFTHS;
asDouble += (double) vTHREE_FIFTHS;
asDecimal += (decimal) vTHREE_FIFTHS;
}
Console.WriteLine("Six Hundred Thousand: {0:F10}", vTHREE_FIFTHS * ONE_MILLION);
Console.WriteLine("Single: {0}", asSingle.ToString("F10"));
Console.WriteLine("Double: {0}", asDouble.ToString("F10"));
Console.WriteLine("Decimal: {0}", asDecimal.ToString("F10"));
The result with the difference hightlighted is:
Three Fifths: 0.6000000000
Six Hundred Thousand: 600000.0000000000
Single: 599093.4000000000
Double: 599999.9999886850
Decimal: 600000.0000000000
vThree Fifths: 0.6000000000
Six Hundred Thousand: 600000.0000000000
Single: 599093.4000000000
Double: 600000.0238418580
Decimal: 600000.0000000000
My question is, can you get C# to get a const float
expression with the equivalent of the runtime (and VB.NET) result? (I.e. produce a THREE_FIFTHS
with the same results as vTHREE_FIFTHS
.)
THREE_FIFTHS
has the same value asvTHREE_FIFTHS
in your example (you can see this withBitConverter.GetBytes
). It only differs in the way it is added to a double in your code.I think your difference is due to the way that the C# compiler handles
const
s as if they were literals, in some ways. E.g. this operation, though it's normally not allowed without a cast, is ok because theconst
lets the compiler see that theint
is small enough to work out:In your case, this means that it doesn't add the single-precision value 3/5 to the double, but calculates the double value 3/5 and adds that. Unfortunately this extra "intelligence" has a side effect. You can get around it by storing the
const float
as afloat
first, e.g.:With this, both ways calculate the double as
600000.0238418580
.You can see more detail on the weirdness with these outputs:
It does look like the answer is "it can't be done", because, as Tim S. pointed out, the compile-time constant is not really constant, but actually reinterpreted at each use. Specifically the
(double)
cast version of the constant produces a different result to the runtime result.Output:
Three Fifths: 0.6000000000
Six Hundred Thousand: 600000.0000000000
Single: 599093.4000000000
Double: 599999.9999886850
Decimal: 600000.0000000000
S9A99193F
#333333333333E33F
D00000006000000000000000000010000
vThree Fifths: 0.6000000000
Six Hundred Thousand: 600000.0000000000
Single: 599093.4000000000
Double: 600000.0238418580
Decimal: 600000.0000000000
S9A99193F
#000000403333E33F
D00000006000000000000000000010000