【问题标题】:Differing casting behavior [duplicate]不同的铸造行为[重复]
【发布时间】:2015-03-25 02:32:29
【问题描述】:

这真的很奇怪。我正在追踪这个错误:

取反二进制补码的最小值无效。

...原来是因为这样的代码:

var valueFromUser = "470259123000000";
var doubleValue = Convert.ToDouble(valueFromUser, CultureInfo.InvariantCulture);
Math.Abs((int)doubleValue);

确实,当我在 LINQPad 中运行它时:

(int)Convert.ToDouble("470259123000000", CultureInfo.InvariantCulture)

...它给了我:

-2147483648

但是,这里的另一位开发人员说他得到了完全不同的东西(不是在 LINQPad 中):

-1141206336

当我尝试仅在常量上单独评估演员表时:

(int)470259123000000.0

...由于需要unchecked,我得到一个编译错误。还有这个:

unchecked((int)470259123000000.0)

...像其他开发人员一样评估为-1141206336。所以我想也许Convert 创造了一个与常量略有不同的值。不,这等于True

Convert.ToDouble("470259123000000", CultureInfo.InvariantCulture) == 470259123000000.0

这到底是怎么回事?为什么评估这些看似相同的表达式会产生如此不同的结果?

更新

找到提示。 4.70259123E14-1141206336 的十六进制表示为:

0x42FABB2BBFA92C00
       0xBBFA92C0

所以我猜其中一个演员是将位直接推入int。所以-2147483648 是更大的谜。

【问题讨论】:

  • 有趣。似乎检查和未检查的铸造行为之间的区别。使用高科技的“计算器”应用程序(当然是在“程序员”模式下;-) 我可以看到-1141206336470259123000000 的截断(32 lsb)相匹配。检查的版本似乎返回int.MinValue。我相信有人会在语言规范中指出这一点。

标签: c# .net casting .net-4.0 double


【解决方案1】:

我不确定根本原因,但它看起来像是一个编译器错误,因为使用 Roslyn 编译的程序为两个表达式提供了相同的值 (-2147483648)。

允许编译器在编译时计算常量表达式。未经检查的表达式的所有转换都由编译器完成,但在另一种情况下,它们是由 CLR 在运行时完成的,因此它们总是有可能使用稍微不同的规则。正如您所观察到的,编译器似乎与运行时截断不同,以将值适合 32 位整数。您可以在底层 IL 中看到,程序只是加载常量值 (0xbbfa92c0) 来代替未经检查的表达式。

using System;
using System.Globalization;

public class Program
{
    public static void Main(string[] args)
    {
        var n = unchecked((int)470259123000000.0);
        Console.WriteLine(n);

        n = (int)Convert.ToDouble("470259123000000", CultureInfo.InvariantCulture);
        Console.WriteLine(n);
    }
}

从 .NET 4.5 编译器反编译的 IL:

  .method public hidebysig static void  Main(string[] args) cil managed
  {
    // 
    .maxstack  2
    .locals init (int32 V_0)
    IL_0000:  nop
    IL_0001:  ldc.i4     0xbbfa92c0
    IL_0006:  stloc.0
    IL_0007:  ldloc.0
    IL_0008:  call       void [mscorlib]System.Console::WriteLine(int32)
    IL_000d:  nop
    IL_000e:  ldstr      "470259123000000"
    IL_0013:  call       class [mscorlib]System.Globalization.CultureInfo [mscorlib]System.Globalization.CultureInfo::get_InvariantCulture()
    IL_0018:  call       float64 [mscorlib]System.Convert::ToDouble(string,
                                                                    class [mscorlib]System.IFormatProvider)
    IL_001d:  conv.i4
    IL_001e:  stloc.0
    IL_001f:  ldloc.0
    IL_0020:  call       void [mscorlib]System.Console::WriteLine(int32)
    IL_0025:  nop
    IL_0026:  ret
  } // end of method Program::Main

【讨论】:

    猜你喜欢
    • 2019-07-07
    • 1970-01-01
    • 1970-01-01
    • 2015-12-26
    • 2011-03-26
    • 2021-06-14
    • 1970-01-01
    • 1970-01-01
    • 2010-09-05
    相关资源
    最近更新 更多