三段常見謬誤
以下三段程式碼,只考慮浮點數為正數之情形,
在面試或口試中,或多或少會遇到,大多的回答也如下所敘。
double x;
1. 如何判斷 x 是否為整數? x == (int)x;
2. 不調用 math.h ,如何取 ceil(x) ? x = (x==(int) x ? x : (int)x+1) ;
3. 不調用 math.h ,如何取 floor(x) ? x = (x==(int) x ? x : (int)x-1) ;
這些答案可能滿足面試者的需求,但實際上之 math library 絕不是這麼做,且過程不算容易。
試想一下,如果 x = 9.34E20 ,結果會如何?
最後結果在硬轉成 int 時,卡在 INT_MAX 沒那麼大,最後便出包。
正常作法,還是該以 IEEE754 欄位分析為佳。
判斷是否為整數
以判斷是否為整數而言,大致上有兩種作法,一種是用 c++ stringstream 方式,
先讀入 string,再導出至 double y ,最後再判斷 x==y 是否成立。
C language 也可先 sprintf 至一 buffer 裡面,再對 buffer 做字串分析,
但 C language 做 sprintf 會不會因精度是 15.95 位關係而出包,這點到沒再研究下去,
有興趣之讀者可自行推導或驗證。
以 IEEE754 欄位分析而言,過程大致如下
1. 取出 mantissa (取16進位)、exponment(取實際代表) 欄位數值。
2. 若 exponment 取出來為負值,代表為小數,直接 return 0
3. 若 exponment 取出來大於等於 0 ,將 mantissa 左移 (exponment + DBL_EXP_BITS) ,若左移後 mantissa == 0ULL ,則傳回 1;反之則傳回 0。
程式碼約如下所敘
is_int.c
/*******************************************************************/
/*
*/
/* filename : is_int.c */
/* author : edison.shih/edisonx */
/* compiler : Visual C++ 2008 */
/* date : 2011.03.07 */
/*
*/
/* A.L.L. R.I.G.H.T.S. R.E.S.E.R.V.E. */
/*
*/
/*******************************************************************/
#include <stdio.h>
#include <math.h>
#define DBL_MAT_BITS 52
#define DBL_SIGN_MASK
0x8000000000000000ULL
#define DBL_EXP_MASK 0x7ff0000000000000ULL
#define DBL_MAT_MASK 0x000fffffffffffffULL
#define DBL_BASE_VALUE
1023
int is_int(double x)
{
unsigned
long long v = *(unsigned long long *)&x;
unsigned
long long mat = ( v & DBL_MAT_MASK );
int exp = (int)((v & DBL_EXP_MASK ) >> (DBL_MAT_BITS))-DBL_BASE_VALUE;
if(v==DBL_SIGN_MASK
|| v==0ULL) return
1;
if(exp < 0) return
0;
mat<<=
(DBL_MAT_BITS + exp);
return
mat==0ULL;
}
int main()
{
double
x[] = {
0.0, 0.4, 0.7, 1.0, 1.2, 2.0,
2.7, 100.0, 100.39, 1234567890.0, 1234567890.1,
-0.0, -0.4, -0.7, -1.0, -1.2,
-2.0, -2.7, -100.0, -100.39, -1234567890.0, -1234567890.1
};
int i, n = sizeof(x) / sizeof(*x);
// check
for(i=0; i<n; ++i){
printf("%+.15e is int ? %d \n", x[i], is_int(x[i]));
}
return
0;
}
is_int 有這問題,ceil / floor 也有這問題。
一個比 ceil / floor 還簡單的 math library 就要弄得這麼麻煩,更別論 ceil / floor 該怎麼做。
結論是,可用 ceil / floor 時,就別認為自己寫 (硬轉型) 的方法會比較快,於是就用自己寫的,
只能說這是為程式碼埋下地雷而已,什麼時候會引爆不知道,就別這麼做了。