1

我尝试了等效的Michael Meadows EDIT 2,但在 VB.NET 中得到了不同的结果。(具体来说,DoubleDecimal结果都是 600000.0238418580。)

我已经确定区别在于存储到 C# 中的float( Single) 除法的编译时准确性float(这似乎更等同于 VB.NET 在存储到 a 时的准确性Double)以及发生的情况(在两种语言中都不足为奇)当您强制除法在运行时发生。

因此,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"));

突出显示差异的结果是:

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

我的问题是,您能否让 C# 获得const float与运行时(和 VB.NET)结果等效的表达式?(即产生THREE_FIFTHS与 相同的结果vTHREE_FIFTHS。)

4

2 回答 2

3

THREE_FIFTHS具有与您的示例相同的值vTHREE_FIFTHS(您可以使用 来查看BitConverter.GetBytes)。它只是在代码中添加到 double 的方式不同。

我认为您的差异是由于 C# 编译器const在某些方面处理 s 的方式就好像它们是文字一样。例如,这个操作,虽然它通常在没有强制转换的情况下是不允许的,但它是可以的,因为它const让编译器看到它int小到可以解决:

const int i = 5;
byte b = i;

在您的情况下,这意味着它不会将单精度值 3/5 添加到双精度值,而是计算双精度值 3/5 并将其相加。不幸的是,这种额外的“智能”有副作用。您可以通过将其存储const floatfloat第一个来解决它,例如:

float f = THREE_FIFTHS;
asDouble += (double) f;

有了这个,两种方式都将双精度计算为600000.0238418580

您可以通过这些输出查看更多关于怪异的详细信息:

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
于 2013-10-25T15:20:31.713 回答
0

看起来答案是“无法完成”,因为正如 Tim S. 所指出的,编译时常量并不是真正的常量,而是在每次使用时都会重新解释。具体来说,(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")));
}

输出:

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

于 2013-10-25T17:18:46.700 回答