[英]Get float constant to be the same the runtime result (and VB.NET)
I tried the equivalent of Michael Meadows EDIT 2 , but in VB.NET and got a different result. 我试过相当于迈克尔梅多斯编辑2 ,但在VB.NET中得到了不同的结果。 (Specifically both the
Double
and Decimal
results were 600000.0238418580.) (特别是
Double
和Decimal
结果都是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. 我已经确定区别在于存储在C#中的
float
中的float
( Single
)除法的编译时精度(这似乎更符合VB.NET在存入Double
时的准确性)以及会发生什么(两者都有)当您强制在运行时进行除法时,语言并不令人惊讶。
So, THREE_FIFTHS
and vTHREE_FIFTHS
provide different results for the asDouble
summation: 因此,
THREE_FIFTHS
和vTHREE_FIFTHS
为asDouble
求和提供了不同的结果:
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 三分之五:60亿
Six Hundred Thousand: 600000.0000000000 六十万:600000.0000000000
Single: 599093.4000000000 单身:599093.4000000000
Double: 599999.9999886850
双人间:599999.9999886850
Decimal: 600000.0000000000 十进制:600000.0000000000
vThree Fifths: 0.6000000000 vThree Fifths:0.6000000000
Six Hundred Thousand: 600000.0000000000 六十万:600000.0000000000
Single: 599093.4000000000 单身:599093.4000000000
Double: 600000.0238418580
双倍:600000.0238418580
Decimal: 600000.0000000000 十进制: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? 我的问题是,你能得到C#来获得一个
const float
表达式,其表达式与运行时(和VB.NET)相当吗? (Ie produce a THREE_FIFTHS
with the same results as vTHREE_FIFTHS
.) (即产生一个
THREE_FIFTHS
,结果与vTHREE_FIFTHS
相同。)
THREE_FIFTHS
has the same value as vTHREE_FIFTHS
in your example (you can see this with BitConverter.GetBytes
). THREE_FIFTHS
具有相同的值vTHREE_FIFTHS
在你的例子(你可以看到这与BitConverter.GetBytes
)。 It only differs in the way it is added to a double in your code. 它只是在代码中添加到double的方式不同。
I think your difference is due to the way that the C# compiler handles const
s as if they were literals, in some ways. 我认为你的不同之处在于C#编译器在某些方面处理
const
的方式,就像它们是文字一样。 Eg this operation, though it's normally not allowed without a cast, is ok because the const
lets the compiler see that the int
is small enough to work out: 例如,这个操作虽然通常在没有强制转换的情况下是不允许的,但是可以,因为
const
让编译器看到int
足够小就可以解决:
const int i = 5;
byte b = i;
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. 在您的情况下,这意味着它不会将单精度值3/5添加到double,而是计算双精度值3/5并添加它。 Unfortunately this extra "intelligence" has a side effect.
不幸的是,这种额外的“情报”有副作用。 You can get around it by storing the
const float
as a float
first, eg: 您可以通过将
const float
作为float
存储来解决它,例如:
float f = THREE_FIFTHS;
asDouble += (double) f;
With this, both ways calculate the double as 600000.0238418580
. 有了这个,两种方法都计算双倍为
600000.0238418580
。
You can see more detail on the weirdness with these outputs: 您可以通过这些输出查看有关奇怪性的更多详细信息:
string GetByteString(double d)
{
return string.Join("", BitConverter.GetBytes(d).Select(b=>b.ToString("X2")));
}
string GetByteString(float f)
{
return string.Join("", BitConverter.GetBytes(f).Select(b=>b.ToString("X2")));
}
double vd = vTHREE_FIFTHS;
double d = THREE_FIFTHS;
const double cd = THREE_FIFTHS;
float f = THREE_FIFTHS;
const double cd2 = 3d / 5d;
double d2 = 3d / 5d;
double df = f;
// doubles
Console.WriteLine(GetByteString((double)THREE_FIFTHS));
Console.WriteLine(GetByteString(vd));
Console.WriteLine(GetByteString(df));
Console.WriteLine(GetByteString(d));
Console.WriteLine(GetByteString(cd));
Console.WriteLine(GetByteString(cd2));
Console.WriteLine(GetByteString(d2));
// floats
Console.WriteLine(GetByteString(f));
Console.WriteLine(GetByteString(vTHREE_FIFTHS));
Console.WriteLine(GetByteString(THREE_FIFTHS));
prints this:
333333333333E33F
000000403333E33F <-- these are the only ones that were actually
000000403333E33F <-- converted from 32-bit float values to doubles
333333333333E33F
333333333333E33F
333333333333E33F
333333333333E33F
9A99193F
9A99193F
9A99193F
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. 看起来答案似乎是“它无法完成”,因为正如Tim S.指出的那样,编译时常量并不是真正的常量,而是在每次使用时实际重新解释。 Specifically the
(double)
cast version of the constant produces a different result to the runtime result. 具体而言,常量的
(double)
强制转换版本会对运行时结果产生不同的结果。
void Main()
{
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(GetByteString((float) THREE_FIFTHS));
Console.WriteLine(GetByteString((double) THREE_FIFTHS));
Console.WriteLine(GetByteString((decimal) THREE_FIFTHS));
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"));
Console.WriteLine(GetByteString((float) vTHREE_FIFTHS));
Console.WriteLine(GetByteString((double) vTHREE_FIFTHS));
Console.WriteLine(GetByteString((decimal) vTHREE_FIFTHS));
}
// Define other methods and classes here
string GetByteString(double d)
{
return "#" + string.Join("", BitConverter.GetBytes(d).Select(b=>b.ToString("X2")));
}
string GetByteString(decimal d)
{
return "D" + string.Join("", Decimal.GetBits(d).Select(b=>b.ToString("X8")));
}
string GetByteString(float f)
{
return "S" + string.Join("", BitConverter.GetBytes(f).Select(b=>b.ToString("X2")));
}
Output: 输出:
Three Fifths: 0.6000000000 三分之五:60亿
Six Hundred Thousand: 600000.0000000000 六十万:600000.0000000000
Single: 599093.4000000000 单身:599093.4000000000
Double: 599999.9999886850
双人间:599999.9999886850
Decimal: 600000.0000000000 十进制:600000.0000000000
S9A99193F S9A99193F
#333333333333E33F
#333333333333E33F
D00000006000000000000000000010000 D00000006000000000000000000010000
vThree Fifths: 0.6000000000 vThree Fifths:0.6000000000
Six Hundred Thousand: 600000.0000000000 六十万:600000.0000000000
Single: 599093.4000000000 单身:599093.4000000000
Double: 600000.0238418580
双倍:600000.0238418580
Decimal: 600000.0000000000 十进制:600000.0000000000
S9A99193F S9A99193F
#000000403333E33F
#000000403333E33F
D00000006000000000000000000010000 D00000006000000000000000000010000
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.