2015-03-25 3 views
8

Это действительно странно. Я выследить эту ошибку:Различное поведение литья

Negating the minimum value of a twos complement number is invalid.

... и оказалось, что это было связано с кодом, как это:

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 

Какого черта здесь происходит? Почему оценка этих, казалось бы, идентичных выражений дает такие разные результаты?

Update:

Найдено подсказку. Шестигранное представление 4.70259123E14 и -1141206336 является:

0x42FABB2BBFA92C00 
     0xBBFA92C0 

Так что я думаю, один из слепков пихают биты непосредственно в int. Итак, -2147483648 - большая тайна.

+0

Интересно. Кажется, разница между проверенным и непроверенным поведением.Использование высокотехнологичного приложения «Калькулятор» (конечно, в режиме «Программист» ;-) Я вижу, что '-1141206336' соответствует усеченному (32 lsb)' 470259123000000'. Проверенная версия возвращает return 'int.MinValue'. Я уверен, что кто-то сможет указать это на языке спецификации. – Alex

ответ

3

Я не совсем уверен в основной причине, но это похоже на ошибку компилятора, потому что программа, скомпилированная с 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); 
    } 
} 

декомпилированные IL из .NET 4.5 компилятора:

.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 
Смежные вопросы