[算法 笔记]大数相乘(续)
原始版本的大数相乘地址: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 }