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

  原始版本的大数相乘地址: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

 

 

你可能感兴趣的:(算法)