2014-03-31 40 views
21

爲什麼Double.MaxValue鑄成一個整數類型會導致負值,即該類型的最小值?Double.MaxValue爲整數是負數?

double maxDouble = double.MaxValue;  // 1.7976931348623157E+308 
long maxDoubleLong = (long) maxDouble; // -9223372036854775808 

我明白一個編譯錯誤,如果它太大或OverflowException在運行時,或者如果我會用unchecked,該轉換可能不會拋出異常,但結果是不確定的和不正確(負)。

而且奇怪的是,該值是long.MinValue

bool sameAsLongMin = maxDoubleLong == long.MinValue; // true 

順便說一句,同樣的情況,如果我將它轉換爲int

int maxDoubleInt = (int)maxDouble;     // -2147483648 
bool sameAsIntMin = maxDoubleInt == int.MinValue; // true 

如果嘗試將其轉換爲decimal我在運行時得到一個OverflowException

decimal maxDoubleDec = (decimal)maxDouble; // nope 

更新:看來,邁克爾和巴里的回答一針見血的頭,如果我使用checked明確我得到一個OverflowException

checked 
{ 
    double maxDouble = double.MaxValue;  // 1.7976931348623157E+308 
    long maxDoubleLong = (long) maxDouble; // nope 
} 
+0

您在代碼註釋中有一個減號後面帶有'maxDoubleInt'的行。 –

回答

13

的C#語言規範(5.0版)說,在6.2以下。 1「顯式數字轉換」(強調):

  • 對於從float或雙爲整型轉換,處理依賴於溢出檢查上下文(§7.6.12),其中 的轉換髮生:

    • 在checked上下文中,轉換過程如下:

      • 如果操作數的值是NaN或無窮大,則引發System.OverflowException。
      • 否則,源操作數向零舍入到最接近的整數值。如果此整數值在 的目標類型範圍內,則此值爲轉換結果。
      • 否則,會引發System.OverflowException。
    • 在未經檢查的上下文中,轉換始終成功,並按以下步驟繼續。

      • 如果操作數的值是NaN或無限,則轉換的結果是目標類型的未指定值。
      • 否則,源操作數向零舍入到最接近的整數值。如果此整數值在 的目標類型範圍內,則此值爲轉換結果。
      • 否則,轉換的結果是目標類型的未指定值。

而在7.6.12 「的選中和未選中運算符」

對於非常量表達式(表達式,在 運行時計算的),該沒有被任何選中或未選中的運算符 或語句包圍,默認溢出檢查上下文未被選中 除非外部因素(如編譯器切換和執行 環境配置)調用檢查評估。

對於從doubledecimal轉換:「如果源值是NaN,無窮大,或過大,無法表示爲小數,則引發System.OverflowException」。 checked vs unchecked不起作用(只處理積分操作)。

+1

是的,我檢查了IL,它使用['conv.i8'](http://msdn.microsoft.com/en-us/library/system.reflection.emit.opcodes.conv_i8%28v=vs.110%它的文檔也明確地聲明**「如果發生溢出轉換浮點類型爲整數,返回的值是未指定的。」** –

+0

是的,你是對的,如果我明確使用'checked'我得到一個'OverflowException'。這是一種奇怪的行爲,不是嗎? –

+1

@TimSchmelter:我猜他們選擇使用'long.MinValue'來幫助錯誤變得更加明顯;這是「快速失敗」哲學的體現。如果你仔細想一想,'long.MaxValue'確實不是更正確的,但它可能會進一步使用該值,顯然不是很明顯。 –

8

也許不是一個完整的答案,但C#語言規範(§6.2.1)這樣說:

在unchecked上下文中,轉換總是會成功,並且 過程如下。

•如果操作數的值是NaN或無限,則 轉換的結果是目標類型的未指定值 。

•否則,源操作數會朝着 四捨五入到最接近的整數值。如果此整數值在 目標類型的範圍內,則此值是 轉換的結果。

•否則,轉換結果爲目標類型的和 未指定值

(強調我的)。

(邁克爾·伯爾回答的同時爲我,他也包含在默認checked/unchecked背景下在C#中,參見下面的註釋信息,所以這個答案,現在主要是多餘的。)

編輯1:請注意,如果轉換完成編譯時間(常量表達式轉換),則規則有點不同。嘗試使用const修改器修改maxDouble變量。 C#編譯器將能夠看到這些值,並且需要您明確地說明unchecked

編輯2:在我的版本的運行時(.NET 4.5 for Windows 8。1),下面的代碼:

double d1 = double.PositiveInfinity; 
double d2 = double.MaxValue; 
double d3 = 2.3e23; 
double d4 = double.NaN; 
double d5 = -2.3e23; 
double d6 = double.MinValue; 
double d7 = double.NegativeInfinity; 
Console.WriteLine((long)d1); 
Console.WriteLine((long)d2); 
Console.WriteLine((long)d3); 
Console.WriteLine((long)d4); 
Console.WriteLine((long)d5); 
Console.WriteLine((long)d6); 
Console.WriteLine((long)d7); 

給出:

-9223372036854775808 
-9223372036854775808 
-9223372036854775808 
-9223372036854775808 
-9223372036854775808 
-9223372036854775808 
-9223372036854775808

所以看來「未指定的值」,其實是「永遠」的目標類型的MinValue,在此實現。

+0

我沒有使用'unchecked'。 –

+0

@TimSchmelter你用過'checked'嗎?除非你(1)在代碼中指定了'checked',或者(2)在你的C#項目文件中設置了一個屬性來檢查所有內容,否則默認值就是'unchecked',這就是你的上下文。 )在'csc.exe'編譯器中使用了一個命令行選項。 –

+0

不,我用默認設置創建了一個空的控制檯應用程序,沒有'checked'或'unchecked'。 –

2

看來這裏的默認行爲是unchecked,即,除非你明確指定checked,溢出未被發現:

double maxDouble = double.MaxValue;  // 1.7976931348623157E+308 
long uncheckedMaxDoubleLong = (long)maxDouble; // -9223372036854775808 
long checkedMaxDoubleLong = checked((long)maxDouble); // ** Overflow Exception 

現在回想起來,試圖從double直接轉換到long而無需驗證或第一約束輸入是不明智的,由於兩個方面:

  • 數值範圍不匹配/潛在溢出
  • 只四捨五入考慮

所以,在這裏一個更好的選擇可能是使用Convert.ToInt64

var convertedDouble = Convert.ToInt64(maxDouble); // ** OverflowException 

由於這種內部做了checked檢查你,並且承擔四捨五入,即意見:

return checked((long)Math.Round(value)); 
+1

如果我在設計一種語言,我會要求所有從浮點類型到整數類型的轉換都是通過方法而不是類型轉換來完成的;而不是讓語言設計者嘗試選擇人們最需要的一組語義,那麼允許程序員說出他們是否寧願讓小數值被困,削減或舍入,號碼行爲應該定期處理或對稱處理,以及他們是否希望超出範圍值,被困或「方便」。 – supercat