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


      原始版本的大数相乘地址: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] != ''; ++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] != ''; ++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] = '';

     

      程序的完整源码:

      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(' ', '
    '), isspace() return 0. */
     30     for ( ; isspace( data[start] )!= 0
     31             && data[start] != ''; ++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] != ''; ++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] != '' )
     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] != ''; ++start )
     71     {
     72         if ( data[start] == '.' )
     73         {
     74             *point_index = start;
     75             break;
     76         }
     77     }
     78 
     79     return ( data[start] != '' );
     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] != ''; ++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] != ''; ++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] = '';
    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] = '';
    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
    ", result );
    226     return 0;
    227 }
    View Code

     

  • 相关阅读:
    phpmyadmin和网页上面的乱码问题
    整理: Android HAL
    warning: deprecated conversion from string constant to ‘char*’ [-Wwrite-strings]-给char* 形参 传入 宏定义字符串
    ubuntu 输入法莫名其妙变繁体
    linux内核版本号添加字符/为何有时会自动添加“+”号以及怎么去掉
    Unable to handle kernel NULL pointer dereference at virtual address 00000000
    Ubuntu 18.04 安装 Samba 服务器及配置
    linux查看版本信息
    图解:电压掉电监测电路如何实现检测工作?
    精密全波整流+一阶RC滤波器检测市电电压
  • 原文地址:https://www.cnblogs.com/life91/p/3397504.html
Copyright © 2020-2023  润新知