2015-12-21 42 views
8

在下面的代碼:C++標準是否允許這種浮點行爲?

#include <cstdint> 
#include <cinttypes> 
#include <cstdio> 

using namespace std; 

int main() { 
    double xd = 1.18; 
    int64_t xi = 1000000000; 

    int64_t res1 = (double)(xi * xd); 

    double d = xi * xd; 
    int64_t res2 = d; 

    printf("%" PRId64"\n", res1); 
    printf("%" PRId64"\n", res2); 
} 

使用v4.9.3 g++ -std=c++14針對32位Windows我得到的輸出:

1179999999 
1180000000 

難道這些值允許不同?

我預計,即使編譯器使用更高的內部精度比doublexi * xd計算,就應該始終如一地做到這一點。浮點轉換中的精度損失爲實現定義的,並且此計算的精度爲實現定義的 - [c.limits]/3表示FLT_EVAL_METHOD應該從C99導入。 IOW我預計它不應該被允許在一條線上使用不同於另一條線上的xi * xd的精度。

注意:這是故意的C++問題而不是C問題 - 我相信這兩種語言在這方面有不同的規則。

+1

請注意,您需要將'xi * xd'作爲'long double'並將'long double'中的整數轉換爲這個錯誤; 1e9 * 1.18比1.18e9大約1/4 ulp。 – tmyklebu

+0

相關:https://gcc.gnu.org/bugzilla/show_bug.cgi?id=323#c127 – Nemo

回答

3

即使編譯器使用更高的內部精度比雙十一爲XD *的計算,它應該這樣做始終

無論是否需要(見下文),這顯然不發生:Stackoverflow充滿了來自同一個程序之內的人看到類似似乎的計算結果沒有表面上的原因的問題。

C++標準n3690草案說(重點煤礦):

的浮動操作數的和值浮動表達式的結果可能在更高的精度和範圍比由所要求的來表示類型;類型不會因此而改變。62

62)演員和作業操作員仍必須執行5.4,5.2.9和5.17中所述的特定轉換。

所以 - 與MM的評論,違反了我先前的編輯協議 - 這是與(double)投版本必須四捨五入到64位double - 這顯然恰好是> = 11.8在問題中記錄的運行 - 在截斷爲整數之前。更一般的情況不會使編譯器自由地在另一種情況下不提前四捨五入。

[c.limits]/3表示FLT_EVAL_METHOD應該從C99導入。 IOW我期望它不應該被允許在一條線上使用不同於另一條線上的xi * xd的精度。

檢查cppreference page

不管FLT_EVAL_METHOD的值,任何浮點表達可以收縮,即,計算爲如果所有中間結果具有無窮大的範圍和精度(除非的#pragma STDC FP_CONTRACT關閉)

由於tmyklebu意見,繼續:

投射和分配剝離任何無關範圍和精度:這模擬了將擴展精度FPU寄存器中的值存儲到標準大小內存位置的操作。

最後一點與標準的「62」部分一致。

M.M.評論:

STDC FP_CONTRACT似乎並沒有出現在C++標準,也很不清楚我到底爲C99行爲是「進口」的程度

沒有出現在選秀我在看。這表明C++並不保證它的可用性,所以上面提到的默認值「任何浮點表達式都可能會收縮」,但我們知道每M.M.評論以及(double)轉換之上的標準和cppreference引用是強制舍入到64位的例外。的<cfloat>

上述C++標準草案說:

的內容是一樣的標準C庫頭。 另見:ISO C 7.1.5,5.2.4.2.2,5.2.4.2.1。

如果這些C類標準的一個必需STDC FP_CONTRACT有更多的它是便攜式由C++程序中使用的機會,但我還沒有調查的支持實現。

+0

該標準的哪一行對應該cppreference行? –

+0

在引用的段落的正下方,cppreference頁面顯示「投射和分配剝離任何無關的範圍和精度:這模擬了將擴展精度FPU寄存器中的值存儲到標準大小內存位置的操作。這在你寫的東西面前飛翔。 – tmyklebu

+0

'STDC FP_CONTRACT'似乎沒有出現在C++標準中,而且我也不清楚C99行爲是在什麼程度上被「導入」的。 –

2

根據FLT_EVAL_METHOD,xi * xd的計算精度可能高於double。如果xi太大以至於無法完全以雙精度表示,那麼我甚至不確定是否允許編譯器精確地將其轉換爲long double或許不是 - 因爲該轉換在之前發生的任何事情都可能發生在之前FLT_EVAL_METHOD。沒有要求必須始終如一地使用更高的精度。

有兩個地方必須轉換爲double:在轉換點(double)和賦值到double處。如果某個值已經「正式」加倍(如xi * xd這裏),即使實際上它的精確度更高,也會有gcc版本將其轉換爲double值。該「優化」總是一個錯誤,因爲一個鑄造必須轉換。

所以,你可能遇到了這個錯誤,其中鑄造翻一番未執行(如果bug仍然存在),你可能會遇到使用不一致的更高的精度,這是合法的,如果FLT_EVAL_METHOD允許它,當FLT_EVAL_METHOD根本不允許它時,甚至可能會遇到不一致的更高精度的使用,這又會是一個錯誤(不是不一致性,而是首先使用更高的精度)。

+0

@MM所以這是編譯器中的一個錯誤,因爲確實存在** [expr.static.cast] 5.2.9 \ 4 **:「這種顯式轉換的效果與執行聲明相同並初始化,然後使用臨時變量作爲轉換的結果。「變量'd'就像是一個臨時的,但表達式的效果是不同的。 –

+0

噢,[那]有多有趣(https://gcc.gnu.org/bugzilla/show_bug.cgi?id=323#c92)! –

相關問題