2011-05-08 25 views
23

GCC產生浮動代碼,對於下面的代碼引發SIGFPEINT_MIN%-1是否會產生未定義的行爲?

#include <limits.h> 
int x = -1; 
int main() 
{ 
    return INT_MIN % x; 
} 

不過,我可以找到在標準中沒有聲明,該代碼調用未定義或實現定義的行爲。據我所知,它需要返回0.這是一個gcc的錯誤,或者我錯過了標準所做的一些特殊例外嗎?

+2

請注意,全局變量是爲了防止gcc優化計算。 – 2011-05-08 01:22:12

+1

因爲試圖獲得負數模的結果,您會期望什麼? – joce 2011-05-08 01:45:50

+0

「當a或n爲負數時,這個幼稚的定義崩潰了,編程語言在這些數值的定義方式上有所不同。」 http://en.wikipedia.org/wiki/Modulo_operation – joce 2011-05-08 01:47:11

回答

15

您可能是對的,這可以被視爲實際標準中的錯誤。所述current draft解決了這個問題:

如果商A/B可表示, 表達式(A/B)* B + A%B應 等於一個;否則, 行爲a/b和a%b都未定義。

+0

接受;這個答案解決了該委員會通過的錯誤和修正。對不起,花了這麼長時間。 – 2011-10-21 02:12:58

+0

該語言是否可能包含在TC到C99中? – caf 2011-11-30 00:16:56

+0

@caf,我的猜測是C99不會有TC,因爲C1x是爲了取代它。 – 2011-11-30 07:52:28

14

綜觀與gcc生成的彙編代碼(x被先前在組件定義爲-1):

movl x, %ecx 
movl $-2147483648, %eax 
movl %eax, %edx 
sarl $31, %edx 
idivl %ecx 

的第一計算指令,sarl,右移-2147483648 31比特。這導致-1被放入%edx

Next idivl被執行。這是一項簽署的操作。讓我引述的描述:

除以包含在所述合併%EDX的雙字的內容:通過在指定的寄存器或存儲器位置的值%eax中的寄存器。

所以-1:-2147483648/-1是發生的部門。 -1:-2147483648解釋爲一個雙字等於-2147483648(在二補數機器上)。現在發生-2147483648/-1,它返回2147483648。繁榮!那是一個多於INT_MAX


關於爲什麼問題,這是一個gcc中的錯誤還是我缺少一些特殊的例外標準?

在這種C99標準是隱式的UB(§6.5.5/ 6):

... /運算的結果是與任何小數部分discarded.88代數商)如果商一/ b可表示,則表達式(a/b)* b + a%b應等於a。

INT_MIN/-1無法表示,因此這是UB。

但是在C89中,%運算符是實現定義的,並且這是否是編譯器錯誤可以辯論。但問題在gcc列出:http://gcc.gnu.org/bugzilla/show_bug.cgi?id=30484

+2

有關* how * SIGFPE正在發生的信息是正確的。但我的擔憂是這是否正確。我從來沒有要求編譯器計算'INT_MIN/-1'(這會明顯導致UB溢出),只是爲了計算'INT_MIN%-1',並且在標準中我沒有發現後者調用UB的語句。仍然是一個翔實的答案。 – 2011-05-08 01:55:22

+0

關於您引用的6.5.5/6:在前一段(5)中,它顯示「%操作符的結果是餘數」。第6段澄清說,當「餘數」可能不明確時,但「a」是「b」整倍數時「剩餘」不含糊;在這種情況下,唯一的餘數是0. – 2011-05-08 02:11:31

+5

@R .:嗯,等待一秒,標準狀態爲「如果商a/b是可表示的」,INT_MIN%-1中不是這種情況。它沒有說明發生這種情況時應該發生什麼,因此它是UB。 – orlp 2011-05-08 02:19:11

9
+0

感謝您提供有用的鏈接。至少它看起來像人們大多同意它是一個錯誤,但是「讓它在默認情況下破壞並且需要一個特殊的選項來修復它」的方法聽起來很荒謬...... – 2011-05-08 01:59:50

+0

是否存在任何簡單的按位/算術表達式,如果'x!= - 1'則計算爲'x'並且如果'x == 1'計算爲'1'?如果是這樣,那麼編譯器可以在執行具有可變分母的'%'之前將其應用於分母,而不是使用相同的分母執行'/'。 – 2011-05-08 02:03:38

+0

@R ..:「默認關閉,但是在標準符合模式下」聽起來沒錯,但這就是錯誤提示。 'gcc'不是一個合格的實現,也不是自稱的。 'gcc -std = c99'也不完全符合,但它確實是...... – 2011-05-08 09:03:46

3

具有負的操作數的模數操作的結果是由§6.5.5/ 6左實現定義在C89,和C99中定義:

&hellip;所述/操作符的結果是代數除去任何小數部分的商數。 88)如果商a/b可表示,則表達(a/b)*b + a%b應等於a

88)這通常被稱爲「截斷朝零」。

對於二進制補碼錶示,INT_MIN/-1等於INT_MAX + 1,所以它不能表示爲一個int無包裝,我想執行選舉離開它爆炸。

+0

然而,'/'運算符沒有出現在我的程序中。不管你如何用負操作數定義結果(「舍入」爲0或向下),-1均勻分割任何東西,因此數學上唯一可能的餘數爲零。 – 2011-05-08 01:56:43

+0

@R ..:我只是引用了關於負操作數行爲的參考。 GCC實現它時計算失敗,所以如果你需要我用這麼多的話來說,它基本上是一個錯誤。 – 2011-05-08 02:59:08

+3

@R ..換言之,'%'僅在定義了'/'時定義,並且由於'/'在這裏是未定義的,'%'也是未定義的,即使它可能不應該。 – 2011-05-08 08:09:36

相關問題