我正在檢查一個double值是否可以表示為int(或對于任何一對浮點整數型別都相同)。這是一個簡單的方法:
double x = ...;
int i = x; // potentially undefined behaviour
if ((double) i != x) {
// not representable
}
但是,它會在標記行上呼叫未定義的行為,并觸發 UBSan(有些人會抱怨)。
問題:
- 這種方法一般被認為可以接受嗎?
- 有沒有一種相當簡單的方法可以在不呼叫未定義行為的情況下做到這一點?
根據要求進行澄清:
我現在面臨的情況涉及從Cdouble中的各種整數型別(int, long, long long)的轉換。但是,我以前遇到過類似的情況,因此我對 float -> integer 和 integer -> float 轉換的答案感興趣。
轉換如何失敗的示例:
- 如果值不是整數,浮點數 -> 整數轉換可能會失敗,例如
3.5. - 源值可能超出目標型別的范圍(大于或小于最大和最小可表示值)。例如
1.23e100. - 源值可能是 -Inf 或 NaN,NaN 很棘手,因為與它的任何比較都會回傳 false。
- 當浮點型別沒有足夠的精度時,整數 -> 浮點轉換可能會失敗。例如,典型的
double有 52 個二進制數字,而 64 位整數型別有 63 個數字。例如,在典型的 64 位系統上,(long) (double) ((1L << 53) 1L). - 我確實理解
1L << 53(相對于(1L << 53) 1)在技術上完全可以表示為 adouble,并且我提出的代碼將接受這種轉換,即使它可能不應該被允許。 - Anything I didn't think of?
uj5u.com熱心網友回復:
完全按照 FP 型別創建范圍限制
“訣竅”是在不失去精度的情況下形成限制。
讓我們考慮float一下int。
float對-2,147,483,648.9999... 到 2,147,483,647.9999... 或接近-1 到 1的轉換int是有效的(例如,使用 32 位 2 的補碼)。int INT_MININT_MAX
我們可以利用它integer_MAX始終是 2 的冪 - 1 并且integer_MIN是 -(power-of-2) (對于常見的 2 的補碼)。
避免限制,FP_INT_MIN_minus_1因為它可能/可能不能完全編碼為 FP。
// Form FP limits of "INT_MAX plus 1" and "INT_MIN"
#define FLOAT_INT_MAX_P1 ((INT_MAX/2 1)*2.0f)
#define FLOAT_INT_MIN ((float) INT_MIN)
if (f < FLOAT_INT_MAX_P1 && f - FLOAT_INT_MIN > -1.0f) {
// Within range.
Use modff() to detect a fraction if desired.
}
更迂腐的代碼會使用!isnan(f)并考慮非 2 的補碼編碼。
uj5u.com熱心網友回復:
使用已知限制和浮點數有效性。limits.h檢查標題里面的內容。
你可以這樣寫:
#include <limits.h>
#include <math.h>
// Of course, constants used are specific to "int" type... There is others for other types.
if ((isnormal(x)) && (x>=INT_MIN) && (x<=INT_MAX) && (round(x)==x))
// Safe assignation from double to int.
i = (int)x ;
else
// Handle error/overflow here.
ERROR(.....) ;
顯然,代碼依賴于惰性布爾評估。
uj5u.com熱心網友回復:
請參閱記憶體中浮點數的 IEEE 754 表示 https://en.wikipedia.org/wiki/IEEE_754
以雙打為例:
- 符號位:1位
- 指數:11 位
- 分數:52 位
這里需要指出三個特殊值:
- 如果指數為0,尾數小數部分為0,則數為±0
- 如果指數為2047,尾數小數部分為0,則數為±∞
- 如果指數為 2047 且尾數的小數部分非零,則數字為 NaN。
這是一個在 64 位上從 double 轉換為 int 的示例,僅供參考
#include <stdint.h>
#define EXPBITS 11
#define FRACTIONBITS 52
#define GENMASK(n) (((uint64_t)1 << (n)) - 1)
#define EXPBIAS GENMASK(EXPBITS - 1)
#define SIGNMASK (~GENMASK(FRACTIONBITS EXPBITS))
#define EXPMASK (GENMASK(EXPBITS) << FRACTIONBITS)
#define FRACTIONMASK GENMASK(FRACTIONBITS)
int double_to_int(double src, int *dst)
{
union {
double d;
uint64_t i;
} y;
int exp;
int sign;
int maxbits;
uint64_t fraction;
y.d = src;
sign = (y.i & SIGNMASK) ? 1 : 0;
exp = (y.i & EXPMASK) >> FRACTIONBITS;
fraction = (y.i & FRACTIONMASK);
// 0
if (fraction == 0 && exp == 0) {
*dst = 0;
return 0;
}
exp -= EXPBIAS;
// not a whole number
if (exp < 0)
return -1;
// out of the range of int
maxbits = sizeof(*dst) * 8 - 1;
if (exp >= maxbits && !(exp == maxbits && sign && fraction == 0))
return -2;
// not a whole number
if (fraction & GENMASK(FRACTIONBITS - exp))
return -3;
// convert to int
*dst = src;
return 0;
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/455487.html
標籤:c overflow undefined-behavior
