2015-08-21 64 views
6

我對MSVC ldexp行爲感到有點驚訝(它發生在Visual Studio 2013中,但也適用於所有舊版本,至少到2003年)。應該正確回合

例如:

#include <math.h> 
#include <stdio.h> 
int main() 
{ 
    double g=ldexp(2.75,-1074); 
    double e=ldexp(3.0,-1074); 

    printf("g=%g e=%g \n",g,e); 
    return 0; 
} 

打印

g=9.88131e-324 e=1.4822e-323 

第一個G是奇怪的圓...
是2.75 * fmin_denormalized,所以我肯定期待第二個結果e。
如果我評估2.75*ldexp(1.0,-1074)我正確得到與e相同的值。

我的期望值是否過高,或者Microsoft不符合某些標準?

回答

5

雖然這個問題沒有明確說明這一點,我認爲由提問者預期的輸出結果是:

g=1.4822e-323 e=1.4822e-323 

這是我們從一個期望C/C++編譯器承諾嚴格遵守IEEE-754。這個問題的標籤都是CC++,我將在這裏寫下C99,因爲這是我掌握的標準。

在附件F,它描述IEC 60559浮點運算(其中,IEC 60559基本上是IEEE-754的別稱)的C99標準規定了:

限定__STDC_IEC_559__的實現應符合 規格在本附件中。 [...]在<math.h>scalbnscalbln和功能 提供在 附錄建議IEC 60559.

該附件在再往下scalb功能,部分F.9.3.6指定:

在二進制系統上,ldexp(x, exp)相當於scalbn(x, exp)

C99標準參考的附錄的1985年版的IEEE-754,在那裏我們找到scalb函數定義如下的附錄:

Scalb(Y,N)返回y× 2 N積分值N無計算2 N

scalb被定義爲乘冪爲2的乘法,並且必須根據標準根據當前舍入模式正確舍入乘法。因此,使用符合C99的編譯器ldexp()必須返回正確舍入的結果如果編譯器定義了__STDC_IEC_559__。在沒有庫調用設置舍入模式時,默認舍入模式「round to nearest or even」有效。

我無權訪問MSVC 2013,因此我不知道它是否定義了該符號。這甚至可能取決於編譯器標誌設置,例如/fp:strict

追查我的C++ 11標準副本之後,我找不到任何關於__STDC_IEC_559__或任何有關IEEE-754綁定的語言。根據對this question的回答,這是因爲通過參考C99標準來包括該支持。

+0

此行爲與/ fp:strict同樣發生。 __STDC_IEC_559__沒有定義,所以它至少不會做出正確的做法......在C/C++中遵循什麼標準的MSVC 2013是另一個問題...... –

1

發生這種情況的原因是,在計算ldexp時,2.75會被截斷爲2,這是因爲在非規格化數量較小的情況下,表示'.75'部分的位移出可表示數字的末尾並消失。無論這是一個錯誤還是設計行爲都可以辯論。

當計算2.75*ldexp(1.0,-1074)正常發生的舍入,和2.75變爲3。

編輯:ldexp應正確圓了,這是一個錯誤。

+3

我認爲OP是特別要求那場辯論。 – Potatoswatter

+1

@Potatoswatter我花了這麼多時間在調試器中發現_Why_這種情況,我忽視了OP的要求。我會爲** bug **投票。 – 1201ProgramAlarm

0

OP結果並不符合C規範,因爲該規範沒有定義計算的精確性。

OP結果可能失敗IEEE 754,但它取決於當時正在使用的舍入模式,它沒有發佈。然而,OP的報告2.75*ldexp(1.0,-1074)按照預期的那樣發揮作用,預期的舍入模式有效。

使用printf("%la",x)有助於清楚地看到double的極限附近發生了什麼。

我希望g「舍入到最接近甚至」的結果爲0x1.8p-1073--這確實發生在windows上的gcc中。

理想g將有0x1.6p-1073

0x0.0p-1073 Zero 
0x0.8p-1073 next higher double DBL_TRUE_MIN 
0x1.0p-1073 next higher double 
0x1.6p-1073 ideal `g` answer, but not available as a double 
0x1.8p-1073 next higher double 

值說句公道話,這可能是一個錯誤的處理器 - 它happened before


參考

double g=ldexp(2.75,-1074); 
printf("%la\n%la\n", 2.75,ldexp(2.75,-1074)); 
printf("%la\n%la\n", 3.0 ,ldexp(3.0 ,-1074)); 
double e=ldexp(3.0,-1074); 
printf("%la\n%la\n", g,e); 
printf("%la\n%la\n", 9.88131e-324, DBL_TRUE_MIN); 
printf("g=%g e=%g \n",g,e); 

0x1.6p+1 
0x1.8p-1073 
0x1.8p+1 
0x1.8p-1073 
0x1.8p-1073 
0x1.8p-1073 
0x1p-1073 
0x1p-1074 
g=1.4822e-323 e=1.4822e-323 
+0

經過調試器中的代碼,這是一個算法錯誤,而不是處理器錯誤。 – 1201ProgramAlarm