簡體   English   中英

當 64 位 int 在 C/C++ 中轉換為 64 位浮點數並且沒有完全匹配時,它是否總是落在非小數上?

[英]When a 64bit int is cast to 64bit float in C/C++ and doesn't have an exact match, will it always land on a non-fractional number?

當 int64_t 被強制轉換為 double 並且沒有完全匹配時,據我所知,我得到了一種等效於 double 的盡力而為最近值。 例如, int64_t 中的 9223372036854775000 似乎以雙精度形式結束:

#include <stdio.h>

int main(int argc, const char **argv) {
    printf("Corresponding double: %f\n", (double)9223372036854775000LL);
    // Outputs: 9223372036854774784.000000
    return 0;
}

在我看來,好像將 int64_t 強制轉換為 double 總是以干凈的非小數結尾,即使在 double 精度非常低的更高數字范圍內也是如此。 但是,我只是從隨機嘗試中觀察到這一點。 對於任何轉換為雙精度的 int64_t 值,是否保證會發生這種情況?

如果我將這個非小數雙精度轉換回 int64_t,我是否總是會得到精確對應的 64 位 int 並將 .0 切掉? 假設它在轉換回來的過程中沒有溢出。 )就像這里:

#include <inttypes.h>
#include <stdio.h>

int main(int argc, const char **argv) {
    printf("Corresponding double: %f\n", (double)9223372036854775000LL);
    // Outputs: 9223372036854774784.000000
    printf("Corresponding int to corresponding double: %" PRId64 "\n",
           (int64_t)((double)9223372036854775000LL));
    // Outputs: 9223372036854774784
    return 0;
}

還是在某些極端情況下它可能不精確並讓我得到“錯誤”的整數?

直觀地說,從我的測試來看,這兩點的答案似乎都是“是”,但如果有人對浮點標准及其背后的數學有很好的正式理解,那么這對我來說真的很有幫助。 如果已知任何已知的更積極的優化(如 gcc 的-Ofast )會破壞其中任何一個,我也會很好奇。

在一般情況下是的,兩者都應該是真的。 The floating point base needs to be - if not 2, then at least integer and given that, an integer converted to nearest floating point value can never produce non-zero fractions - either the precision suffices or the lowest-order integer digits in the base浮動類型的將被歸零。 例如,在您的情況下,您的系統使用 ISO/IEC/IEEE 60559 二進制浮點數。 在以 2 為底進行檢查時,可以看出該值的尾隨數字確實為零:

>>> bin(9223372036854775000)
'0b111111111111111111111111111111111111111111111111111110011011000'
>>> bin(9223372036854774784)
'0b111111111111111111111111111111111111111111111111111110000000000'

考慮到 double 的值落在 integer 類型的范圍內,將不帶小數的 double 轉換為 integer 類型應該是精確的......

盡管您仍然可能會遇到實現質量問題或徹底的錯誤 - 例如, MSVC當前有一個編譯器錯誤,其中設置了 MSB 的無符號 32 位值的往返轉換(或只是 2³¹ 和 2³² 之間的雙精度值-1 轉換為unsigned int ) 將在轉換中“溢出”,並且總是導致正好 2³¹。

以下假設被轉換的值為正。 負數的行為是類似的。

C 2018 6.3.1.4 2 指定從 integer 到真實的轉換並說:

…如果要轉換的值在可以表示但不能精確表示的值范圍內,則結果是最接近的較高或最近的較低可表示值,以實現定義的方式選擇。

這告訴我們,僅當邊界x的兩個可表示值之一不是 integer 並且x不可表示時,某些 integer 值x被轉換為浮點數才能產生非整數。

5.2.4.2.2 指定了用於浮點數的 model。 每個有限浮點數都由某個基數b中的數字序列表示,該數字序列針對某個指數eb e縮放。 b是大於 1 的 integer 。)然后,如果限制x的兩個值之一,例如p不是 integer,則縮放比例必須使得該浮點數中的最低位表示分數。 但如果是這種情況,則將p中表示分數的所有數字設置為 0 必須生成一個新的浮點數,即 integer。 如果x < p ,這個 integer 必須是x ,因此x可以用浮點格式表示。 另一方面,如果p < x ,我們可以將足夠的數字加到表示分數的每個數字上,使其為 0(並產生下一個更高數字的進位)。 這也將產生一個 integer 可表示為浮點類型1 ,它必須是x

因此,如果將 integer x轉換為浮點類型會產生非整數,則x必須可以在該類型中表示。 但是隨后轉換為浮點類型必須產生x 所以永遠不可能產生一個非整數。

腳注

1這可能會執行所有數字,例如將其應用於三位十進制數 9.99,產生 10.00。 在這種情況下,如果它在浮點格式的范圍內,則生成的值是b的下一個冪。 如果不是,則 C 標准未定義該行為。 另請注意,C 標准對浮點格式必須支持的范圍設置了最低要求,這排除了任何格式無法表示 1,這避免了轉換可能產生類似 999 的數字的退化情況,因為它是最大可表示的有限值。

當 64 位int被轉換為 64 位浮點數......並且沒有完全匹配時,它是否總是落在非小數上?
對於任何轉換為doubleint64_t值,這是否保證會發生?

對於common double :是的,它總是落在一個非小數上

當不匹配時,結果是上面或下面最接近的浮點可表示值,具體取決於舍入模式。 鑒於 common double的特性,這兩個邊界值也是整數。 當該值不可表示時,首先有一個附近的整數 1。


...如果我將這個非小數double精度轉換回int64_t ,我是否總是會得到精確對應的 64 位int並將 .0 切掉?

不會。 INT64_MAX附近的邊緣情況會失敗,因為轉換后的值可能會變成高於INT64_MAX的 FP 值。 然后轉換回 integer 類型會導致:“新類型是有符號的,並且值不能在其中表示;結果是實現定義的,或者引發了實現定義的信號。” C17dr § 6.3.1.3 3

#include <limits.h>
#include <string.h>

int main() {
  long long imaxm1 = LLONG_MAX - 1;
  double max = (double) imaxm1;
  printf("%lld\n%f\n", imaxm1, max);
  long long imax = (long long) max;
  printf("%lld\n", imax);
}

9223372036854775806
9223372036854775808.000000
9223372036854775807  // Value here is implementation defined.

更深層次的例外

(問題變體)當 N 位 integer 類型被強制轉換為浮點並且沒有精確匹配時,它是否總是落在非小數上?

Integer 類型范圍超過有限浮點

轉換為無窮大:使用常見的floatuint128_tUINT128_MAX轉換為無窮大 這很容易通過超寬 integer 類型實現。

int main() {
  unsigned __int128  imaxm1 = 0xFFFFFFFFFFFFFFFF;
  imaxm1 <<= 64;
  imaxm1 |= 0xFFFFFFFFFFFFFFFF;
  double fmax = (float) imaxm1;
  double max = (double) imaxm1;
  printf("%llde27\n%f\n%f\n", (long long) (imaxm1/1000000000/1000000000/1000000000), 
    fmax, max);
}

340282366920e27
inf
340282366920938463463374607431768211456.000000

浮點進動深度超過范圍

在某些 unicorn 實現中,FP 精度非常寬且范圍小,最大的有限項在理論上(而不是實踐)可能是非整數。 然后使用更寬的 integer 類型,轉換可能會導致這個非整數值。 我不認為這是 OP 的合法問題。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM