[回目錄]


三段常見謬誤

 

以下三段程式碼,只考慮浮點數為正數之情形,

在面試或口試中,或多或少會遇到,大多的回答也如下所敘。

 

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 時,就別認為自己寫 (硬轉型) 的方法會比較快,於是就用自己寫的,

只能說這是為程式碼埋下地雷而已,什麼時候會引爆不知道,就別這麼做了。

 

[回目錄]

arrow
arrow
    全站熱搜

    edisonx 發表在 痞客邦 留言(3) 人氣()