简体   繁体   English

获取浮点常量与运行时结果相同(和VB.NET)

[英]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.) (特别是DoubleDecimal结果都是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中的floatSingle )除法的编译时精度(这似乎更符合VB.NET在存入Double时的准确性)以及会发生什么(两者都有)当您强制在运行时进行除法时,语言并不令人惊讶。

So, THREE_FIFTHS and vTHREE_FIFTHS provide different results for the asDouble summation: 因此, THREE_FIFTHSvTHREE_FIFTHSasDouble求和提供了不同的结果:

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM