[算法 笔记]大数相乘(续)

  原始版本的大数相乘地址:http://www.cnblogs.com/life91/p/3389890.html

  在原来的版本中,整数相乘与小数相乘是分开的。其中,在计算小数时,需要将数值与小数点进行分割。在本次版本中,整数和小数在一次计算中进行处理。

本版本中对原始版本中的几个BUG进行处理:

  1. 小数末尾出现的无效0。例如,”0.123400” -> “0.1234”

  2. 对于两个均是小于0的小数相乘,需要在结果中的整数部分存放’0’。例如,0.12*0.4 = 0.048

  参考小数相乘模型:

    2 . 5

   × 1 . 2

    ---------------- ---- 数值上标数值表示进位值

          51  0            ---- 被乘数中小数点需要滤过

                           ---- 乘数中小数点的计算序列

     2    5

   -----------------

     31  0    0             ---- 计算结果中小数点的滤过

   在计算过程中,被乘数中出现小数点需要滤过的只是本次计算,再次计算整数位;而乘数需要滤过的是与整个被乘数的计算过程。在结果存放过程中,如果当前位为小数点,则仅仅向前进一位,即将结果存放在整数部分。

 1   for ( i = 0; lhs[i] != '\0'; ++i )
 2       {
 3           int tmp0 = lhs[i] - '0';
 4           res_i = tmp_i;
 5           // 滤过乘数的小数点
 6           if ( lhs[i] == '.' )
 7           {
 8               continue;
 9           }
10   
11           for ( j = 0; rhs[j] != '\0'; ++j )
12           {
13               int tmp1 = rhs[j] - '0';
14   
15               // 滤过被乘数的小数点,但是需要记录当前的进位值
16               if ( rhs[j] == '.' )
17               {
18                   continue;
19               }
20   
21               // 滤过结果中的小数点。
22               if ( result[res_i] == '.' )
23                   ++res_i;
24   
25               carry += ( result[res_i] - '0' + tmp0 * tmp1 );
26               result[res_i++] = carry % 10 + '0';
27               carry /= 10;
28           }
29   
30           while ( carry )
31           {
32               if ( result[res_i] == '.' )
33                   ++res_i;
34   
35               result[res_i++] = carry % 10 + '0';
36               carry /= 10;
37           }
38   
39           // 如果乘数中有小数点,则这个落后于乘数的字符个数。
40           ++tmp_i;
41       }

  如何处理小数末尾部分出现的无效0?在刚刚调用子函数计算的结果中,存放顺序是从低位到高位的。因此,可以首先统计末尾部分的0字符的个数,然后在翻转后的结果中进行截断处理。

1   // 删除小数点后多余的零值
2       for ( ; zero_cnt < result_point_index && result[zero_cnt] == '0';
3               ++zero_cnt );
4   
5   // .......
6   
7   // 结果值翻转
8       reverse_data( result, 0, result_length );
9       result[result_length - zero_cnt] = '\0';

 

  程序的完整源码:

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <string.h>
  4 #include <assert.h>
  5 #include <ctype.h>
  6 
  7 // 翻转data[start...end-1]
  8 void reverse_data( char *data, int start, int end  )
  9 {
 10     char temp = '0';
 11 
 12     assert( data != NULL && start < end );
 13     while ( start < end )
 14     {
 15         temp = data[start];
 16         data[start] = data[--end];
 17         data[end] = temp;
 18         ++start;
 19     }
 20 }
 21 
 22 int check_logic( const char *data, int *nonspace_index )
 23 {
 24     int flag = 1;
 25     int start = 0;
 26     int point_cnt = 0;
 27 
 28     assert( data != NULL );
 29     /* PS. if data is not space(' ', '\n'), isspace() return 0. */
 30     for ( ; isspace( data[start] )!= 0
 31             && data[start] != '\0'; ++start );
 32 
 33     // 判断数据是否为负数
 34     *nonspace_index = start;
 35     if ( data[start] == '-' || data[start] == '+' )
 36     {
 37         ++start;
 38     }
 39 
 40     /* PS. if ch is digit character, isdigit() return 1; otherwise return 0. */
 41     for ( ; data[start] != '\0'; ++start )
 42     {
 43         if ( isdigit( data[start] ) || data[start] == '.' )
 44         {
 45             // 判断数据为小数的格式是否正确。
 46             if ( data[start] == '.' && point_cnt == 0 )
 47             {
 48                 ++point_cnt;
 49             }
 50             else if ( point_cnt > 1 )
 51             {
 52                 break;
 53             }
 54         }
 55     }
 56 
 57     // 若小数点后面无数据,则不合法
 58     if ( data[start] != '\0' )
 59     {
 60         flag = 0;
 61     }
 62 
 63     return flag;
 64 }
 65 
 66 int has_point( char *data, int index, int *point_index )
 67 {
 68     int start = index;
 69 
 70     for ( ; data[start] != '\0'; ++start )
 71     {
 72         if ( data[start] == '.' )
 73         {
 74             *point_index = start;
 75             break;
 76         }
 77     }
 78 
 79     return ( data[start] != '\0' );
 80 }
 81 
 82 int is_neg( char *data, int *index )
 83 {
 84     int flag = 0;
 85     int start = *index;
 86     if ( data[start] == '-' || data[start] == '+' )
 87     {
 88         if ( data[start] == '-' )
 89             flag = 1;
 90         ++start;
 91     }
 92 
 93     *index = start;
 94     return flag;
 95 }
 96 
 97 int compute_value_opt( char *lhs, char *rhs, char *result )
 98 {
 99     int i = 0, j = 0, res_i = 0;
100     int tmp_i = 0;
101     int carry = 0;
102 
103     assert( lhs != NULL && rhs != NULL && result != NULL );
104 
105     for ( i = 0; lhs[i] != '\0'; ++i )
106     {
107         int tmp0 = lhs[i] - '0';
108         res_i = tmp_i;
109         // 滤过乘数的小数点
110         if ( lhs[i] == '.' )
111         {
112             continue;
113         }
114 
115         for ( j = 0; rhs[j] != '\0'; ++j )
116         {
117             int tmp1 = rhs[j] - '0';
118 
119             // 滤过被乘数的小数点,但是需要记录当前的进位值
120             if ( rhs[j] == '.' )
121             {
122                 continue;
123             }
124 
125             // 滤过结果中的小数点。
126             if ( result[res_i] == '.' )
127                 ++res_i;
128 
129             carry += ( result[res_i] - '0' + tmp0 * tmp1 );
130             result[res_i++] = carry % 10 + '0';
131             carry /= 10;
132         }
133 
134         while ( carry )
135         {
136             if ( result[res_i] == '.' )
137                 ++res_i;
138 
139             result[res_i++] = carry % 10 + '0';
140             carry /= 10;
141         }
142 
143         // 如果乘数中有小数点,则这个落后于乘数的字符个数。
144         ++tmp_i;
145     }
146 
147     result[res_i] = '\0';
148 
149     return res_i;
150 }
151 
152 int big_number_multiply_opt( char *lhs, char *rhs, char *result )
153 {
154     int lhs_digit_start = 0, rhs_digit_start = 0;
155     int lhs_point_index = 0, rhs_point_index = 0;
156     int lhs_len = 0, rhs_len = 0;
157     int result_is_neg = 0;
158     int result_length = 0;
159     int result_point_index = 0;
160     int zero_cnt = 0;
161 
162     assert( lhs != NULL && rhs != NULL && result != NULL );
163 
164     // 检查数据的合法性
165     if ( !(check_logic( lhs, &lhs_digit_start )
166             && check_logic( rhs, &lhs_digit_start )) )
167     {
168         return -1;
169     }
170 
171     // 检查数据是否为负数
172     result_is_neg = is_neg( lhs, &lhs_digit_start );
173     if ( is_neg( rhs, &lhs_digit_start) )
174     {
175         result_is_neg = result_is_neg == 1 ?  0 : 1;
176     }
177 
178     // 计算结果中,小数点的位置
179     has_point( lhs, lhs_digit_start, &lhs_point_index );
180     has_point( rhs, rhs_digit_start, &rhs_point_index );
181     lhs_len = strlen( lhs );
182     rhs_len = strlen( rhs );
183     result_point_index =  lhs_len - lhs_point_index
184                             + rhs_len - rhs_point_index - 2;
185     result[result_point_index] = '.';
186 
187     // 计算大数值
188     reverse_data( lhs, lhs_digit_start, lhs_len );
189     reverse_data( rhs, rhs_digit_start, rhs_len );
190     result_length = compute_value_opt( lhs + lhs_digit_start,
191                                        rhs + rhs_digit_start, result );
192     reverse_data( rhs, rhs_digit_start, rhs_len );
193     reverse_data( lhs, lhs_digit_start, lhs_len );
194 
195     // 删除小数点后多余的零值
196     for ( ; zero_cnt < result_point_index && result[zero_cnt] == '0';
197             ++zero_cnt );
198 
199     // 需要注意的是,在0.xx与0.xx相乘时,考虑最开始部分需要存在一个0.
200     if ( result_length == result_point_index )
201     {
202         result[result_length++] = '0';
203     }
204 
205     // 对小数赋值的处理。
206     if ( result_is_neg )
207         result[result_length++] = '-';
208 
209     // 结果值翻转
210     reverse_data( result, 0, result_length );
211     result[result_length - zero_cnt] = '\0';
212 
213     return result_length - zero_cnt;
214 }
215 
216 int main()
217 {
218     char lhs[] = "-0.223456";
219     char rhs[] = "0.5";
220     char result[40];
221 
222     memset( result, '0', sizeof(result) );
223 
224     big_number_multiply_opt( lhs, rhs, result );
225     printf( "%s\n", result );
226     return 0;
227 }
View Code

 

 

posted @ 2013-10-30 18:57  life91  阅读(248)  评论(0编辑  收藏  举报