2016-02-28 377 views
0

我搜索了文檔,但找不到任何解釋。爲什麼溢出結果爲0?

我有一個d程序:

import std.stdio; 
void main() { 
    writeln(int.max); 
    int a = 2; 
    int b = 180; 
    writeln(a^^b); 
} 

它寫道:

2147483647 
0 

我溢出int,而是獲得垃圾或包裹,我得到0

如果我使用realdouble,顯然輸出是正確的。

我寫了一個程序進行實驗,這在C(不是C是d,但d編譯爲本地代碼和C是可移植的彙編程序,所以他們應該是相當的):

#include <stdio.h> 
#include <math.h> 
#include <limits.h> 
int main(void) { 
    double i = pow(2, 128); 
    int j = (int) i; 
    printf("int max:   %d\n", INT_MAX); 
    printf("print dbl as int: %d\n", i); 
    printf("cast dbl -> int: %d\n", j); 
    printf("double:   %f\n", i); 
    return 0; 
} 

它提供:

int max:   2147483647 
print dbl as int: -1254064128 
cast dbl -> int: -2147483648 
double:   340282366920938463463374607431768211456.000000 

第二行和第三行線很少會發生同樣的事情兩次,因爲我相信這是不確定的行爲,這是一點。

我知道D想成爲一個更好的C,並且一種方法是消除未定義的行爲。但是,如果D是系統編程語言(它甚至有內聯asm),爲什麼D拒絕在溢出時換行?

回答

4

它在溢出時換行。你恰好碰巧試圖將發生在零的情況下。嘗試3^^180。我得到了-949019631。僅僅因爲屏幕上看起來很漂亮的數字並不意味着它不是垃圾!

考慮2 ^^ n == 1 < < n。當你一遍又一遍地移動一個位時會發生什麼?最終,右側的所有位都變爲零!然後,當你截斷它以適應64位的值時,你剩下的都是零。


但讓我進入一些細節無論如何。首先,你的C的批評:

// snip. note that i is type double 
    printf("print dbl as int: %d\n", i); 

這條線是錯在兩個層面上:它通過一個64位雙,其中的printf期待一個32位的int,它是重新解釋鑄造這些位爲int,這是完全不同於轉換爲int。

如果您想在D中執行此操作,您希望使用聯合或轉換通過中間指針顯式重新解釋這些位。如果你想的話,你甚至可以切掉其他32位!

下一行使用正確的顯式強制轉換寫入正確,但仍然未定義行爲,因爲當它太大而無法放入時將double轉換爲int是既不是C也不是D(也不是底層硬件)承諾。


再換D的^^操作者簡單地d重寫到表達std.math.pow(a, b)std.math.pow對不同類型有不同的實現。由於這兩個參數都是整數,因此它根本不會執行浮點計算 - 它具有純粹的整型/長整型實現,就像乘法一樣工作。

所以你的C比較並不完全正確,因爲在C中,你使用了double並試圖進行轉換,而在D中,它從來沒有觸及過浮點。整數乘法被定義爲通過二進制補碼和截斷來工作,而這正是發生在這裏的事情。它溢出了,剩下的位都留下了全部的零。

+0

只需要注意一下:C中兩行不正確的點有意未定義/不正確,以證明我期待的結果。但是,現在我明白了,非常感謝。 – cat

+1

「它將重新解釋將這些位轉換爲int」 - 在C中,它是*未定義的行爲*。結果(如果有的話)可能與輸入不相似,例如,在常見的64位系統上,使用不同於整數值的寄存器將浮點值傳遞給printf –

+0

是的,當然,這就是爲什麼它是如此隨機! –