最近尝试用Cocoa做一个四则运算计算器来练手,类似于Windows cmd: calc的那种。

毕竟这个东西算是我们项目组的入门练习,当年很多新人刚进来,老组长都会教他们用MFC/QT做个计算器来看看水平。由于各种原因,我当年倒是没有受到这种“礼遇”,等我真正开始做软件的时候,还是服务器端这样的纯C++代码做得比较多。但是不做不知道,一做才发现UI的逻辑还是挺复杂的,比如说,按“1”是追加到当前显示还是覆盖现有的显示呢,最后把逻辑弄清楚倒不是很麻烦,但要写出好看的代码还是有比较大的差距的。

以上都是前言,好像比较长……

要说的是,在这个过程中发现的问题,计算结果是浮点数,要怎么判断其是否能无损地转换成整数,从而消除小数点后的一段无用的“0”,以更好地显示。直接上代码:
 1  template  < typename FloatType >
 2  struct  _floattype_meta
 3  {
 4  };
 5 
 6  template <>
 7  struct  _floattype_meta < float >
 8  {
 9       enum  {
10          EXPO_OFFSET  =   23 ,
11          EXPO_LEN  =   8
12      };
13 
14      typedef uint32_t match_uint_type;
15  };
16 
17  template <>
18  struct  _floattype_meta < double >
19  {
20       enum  {
21          EXPO_OFFSET  =   52 ,
22          EXPO_LEN  =   11
23      };
24 
25      typedef uint64_t match_uint_type;
26  };
27 
28  template  < typename FloatType >
29  struct  float_to_int
30  {
31      typedef  struct  _floattype_meta < FloatType >  _meta;
32 
33       bool   operator () ( FloatType f, FloatType precision )
34      {
35           static   const  _meta::match_uint_type EXPO_MASK  =  
36              ( ~ (( ~ (_meta::match_uint_type) 0 <<  _meta::EXPO_LEN ))  <<  _meta::EXPO_OFFSET;
37          _meta::match_uint_type *  pf  =  (_meta::match_uint_type * ) & f;
38          uint32_t expo  =  (( * pf)  &  EXPO_MASK)  >>  _meta::EXPO_OFFSET;
39 
40           static   const  uint32_t EXPO_FIRSTBIT_MASK  =   1 <<  (_meta::EXPO_LEN - 1 );
41           static   const  uint32_t EXPO_BOUND  =  EXPO_FIRSTBIT_MASK  -   1 ;
42           if  ( expo  >=  EXPO_BOUND )
43          {
44              uint32_t to_right_move  =  expo  -  EXPO_BOUND;
45               if  ( to_right_move  >=  _meta::EXPO_OFFSET )
46              {
47                   return   true ;
48              }
49              uint32_t cmp_len  =  _meta::EXPO_OFFSET  -  to_right_move;
50              _meta::match_uint_type mask  =   ~ ( ~ ((_meta::match_uint_type) 0 <<  cmp_len);
51               return  ( * pf & mask)  ?   false  :  true ;
52          }
53           else
54          {
55               return  ( f  <  precision  &&  f  >   - precision )  ?   true  :  false ;
56          }
57      }
58  };

我的方法是通过浮点型的结构来进行判断。

浮点类型一般结构如下:
|+/-|      exponent       |             tail                 |
对于float,指数部分为8字节,尾数部分为23字节。
对于double,指数部分为11字节,尾数部分为52字节。
其中指数部分是采用偏移方式的,比如float的指数部分为130,偏移值为127,即实际指数为130-127。
更详细的请参考 这里。

判断方法是,计算指数的值,根据偏移判断小数点后的尾数,想得比较简单,汗!
引入参数精度是为了判断值小于1时达到某个阈值的时候可以将后面的小数略去。

Honestly,其实这个应该可以用sprintf,然后判断小数点后的“0”来实现的,似乎更加简单方便。
但是我觉得,作为一个“码农”,重造轮子也是一种趣味嘛!