xref: /haiku/src/libs/mapm/mapm_rcp.c (revision 59d799dabcba86f92658ddb402f634e262d9aae7)
1*59d799daSIngo Weinhold 
2*59d799daSIngo Weinhold /*
3*59d799daSIngo Weinhold  *  M_APM  -  mapm_rcp.c
4*59d799daSIngo Weinhold  *
5*59d799daSIngo Weinhold  *  Copyright (C) 2000 - 2007   Michael C. Ring
6*59d799daSIngo Weinhold  *
7*59d799daSIngo Weinhold  *  Permission to use, copy, and distribute this software and its
8*59d799daSIngo Weinhold  *  documentation for any purpose with or without fee is hereby granted,
9*59d799daSIngo Weinhold  *  provided that the above copyright notice appear in all copies and
10*59d799daSIngo Weinhold  *  that both that copyright notice and this permission notice appear
11*59d799daSIngo Weinhold  *  in supporting documentation.
12*59d799daSIngo Weinhold  *
13*59d799daSIngo Weinhold  *  Permission to modify the software is granted. Permission to distribute
14*59d799daSIngo Weinhold  *  the modified code is granted. Modifications are to be distributed by
15*59d799daSIngo Weinhold  *  using the file 'license.txt' as a template to modify the file header.
16*59d799daSIngo Weinhold  *  'license.txt' is available in the official MAPM distribution.
17*59d799daSIngo Weinhold  *
18*59d799daSIngo Weinhold  *  This software is provided "as is" without express or implied warranty.
19*59d799daSIngo Weinhold  */
20*59d799daSIngo Weinhold 
21*59d799daSIngo Weinhold /*
22*59d799daSIngo Weinhold  *      $Id: mapm_rcp.c,v 1.7 2007/12/03 01:46:46 mike Exp $
23*59d799daSIngo Weinhold  *
24*59d799daSIngo Weinhold  *      This file contains the fast division and reciprocal functions
25*59d799daSIngo Weinhold  *
26*59d799daSIngo Weinhold  *      $Log: mapm_rcp.c,v $
27*59d799daSIngo Weinhold  *      Revision 1.7  2007/12/03 01:46:46  mike
28*59d799daSIngo Weinhold  *      Update license
29*59d799daSIngo Weinhold  *
30*59d799daSIngo Weinhold  *      Revision 1.6  2003/07/21 20:20:17  mike
31*59d799daSIngo Weinhold  *      Modify error messages to be in a consistent format.
32*59d799daSIngo Weinhold  *
33*59d799daSIngo Weinhold  *      Revision 1.5  2003/05/01 21:58:40  mike
34*59d799daSIngo Weinhold  *      remove math.h
35*59d799daSIngo Weinhold  *
36*59d799daSIngo Weinhold  *      Revision 1.4  2003/03/31 22:15:49  mike
37*59d799daSIngo Weinhold  *      call generic error handling function
38*59d799daSIngo Weinhold  *
39*59d799daSIngo Weinhold  *      Revision 1.3  2002/11/03 21:32:09  mike
40*59d799daSIngo Weinhold  *      Updated function parameters to use the modern style
41*59d799daSIngo Weinhold  *
42*59d799daSIngo Weinhold  *      Revision 1.2  2000/09/26 16:27:48  mike
43*59d799daSIngo Weinhold  *      add some comments
44*59d799daSIngo Weinhold  *
45*59d799daSIngo Weinhold  *      Revision 1.1  2000/09/26 16:16:00  mike
46*59d799daSIngo Weinhold  *      Initial revision
47*59d799daSIngo Weinhold  */
48*59d799daSIngo Weinhold 
49*59d799daSIngo Weinhold #include "m_apm_lc.h"
50*59d799daSIngo Weinhold 
51*59d799daSIngo Weinhold /****************************************************************************/
m_apm_divide(M_APM rr,int places,M_APM aa,M_APM bb)52*59d799daSIngo Weinhold void	m_apm_divide(M_APM rr, int places, M_APM aa, M_APM bb)
53*59d799daSIngo Weinhold {
54*59d799daSIngo Weinhold M_APM   tmp0, tmp1;
55*59d799daSIngo Weinhold int     sn, nexp, dplaces;
56*59d799daSIngo Weinhold 
57*59d799daSIngo Weinhold sn = aa->m_apm_sign * bb->m_apm_sign;
58*59d799daSIngo Weinhold 
59*59d799daSIngo Weinhold if (sn == 0)                  /* one number is zero, result is zero */
60*59d799daSIngo Weinhold   {
61*59d799daSIngo Weinhold    if (bb->m_apm_sign == 0)
62*59d799daSIngo Weinhold      {
63*59d799daSIngo Weinhold       M_apm_log_error_msg(M_APM_RETURN, "\'m_apm_divide\', Divide by 0");
64*59d799daSIngo Weinhold      }
65*59d799daSIngo Weinhold 
66*59d799daSIngo Weinhold    M_set_to_zero(rr);
67*59d799daSIngo Weinhold    return;
68*59d799daSIngo Weinhold   }
69*59d799daSIngo Weinhold 
70*59d799daSIngo Weinhold /*
71*59d799daSIngo Weinhold  *    Use the original 'Knuth' method for smaller divides. On the
72*59d799daSIngo Weinhold  *    author's system, this was the *approx* break even point before
73*59d799daSIngo Weinhold  *    the reciprocal method used below became faster.
74*59d799daSIngo Weinhold  */
75*59d799daSIngo Weinhold 
76*59d799daSIngo Weinhold if (places < 250)
77*59d799daSIngo Weinhold   {
78*59d799daSIngo Weinhold    M_apm_sdivide(rr, places, aa, bb);
79*59d799daSIngo Weinhold    return;
80*59d799daSIngo Weinhold   }
81*59d799daSIngo Weinhold 
82*59d799daSIngo Weinhold /* mimic the decimal place behavior of the original divide */
83*59d799daSIngo Weinhold 
84*59d799daSIngo Weinhold nexp = aa->m_apm_exponent - bb->m_apm_exponent;
85*59d799daSIngo Weinhold 
86*59d799daSIngo Weinhold if (nexp > 0)
87*59d799daSIngo Weinhold   dplaces = nexp + places;
88*59d799daSIngo Weinhold else
89*59d799daSIngo Weinhold   dplaces = places;
90*59d799daSIngo Weinhold 
91*59d799daSIngo Weinhold tmp0 = M_get_stack_var();
92*59d799daSIngo Weinhold tmp1 = M_get_stack_var();
93*59d799daSIngo Weinhold 
94*59d799daSIngo Weinhold m_apm_reciprocal(tmp0, (dplaces + 8), bb);
95*59d799daSIngo Weinhold m_apm_multiply(tmp1, tmp0, aa);
96*59d799daSIngo Weinhold m_apm_round(rr, dplaces, tmp1);
97*59d799daSIngo Weinhold 
98*59d799daSIngo Weinhold M_restore_stack(2);
99*59d799daSIngo Weinhold }
100*59d799daSIngo Weinhold /****************************************************************************/
m_apm_reciprocal(M_APM rr,int places,M_APM aa)101*59d799daSIngo Weinhold void	m_apm_reciprocal(M_APM rr, int places, M_APM aa)
102*59d799daSIngo Weinhold {
103*59d799daSIngo Weinhold M_APM   last_x, guess, tmpN, tmp1, tmp2;
104*59d799daSIngo Weinhold char    sbuf[32];
105*59d799daSIngo Weinhold int	ii, bflag, dplaces, nexp, tolerance;
106*59d799daSIngo Weinhold 
107*59d799daSIngo Weinhold if (aa->m_apm_sign == 0)
108*59d799daSIngo Weinhold   {
109*59d799daSIngo Weinhold    M_apm_log_error_msg(M_APM_RETURN, "\'m_apm_reciprocal\', Input = 0");
110*59d799daSIngo Weinhold 
111*59d799daSIngo Weinhold    M_set_to_zero(rr);
112*59d799daSIngo Weinhold    return;
113*59d799daSIngo Weinhold   }
114*59d799daSIngo Weinhold 
115*59d799daSIngo Weinhold last_x = M_get_stack_var();
116*59d799daSIngo Weinhold guess  = M_get_stack_var();
117*59d799daSIngo Weinhold tmpN   = M_get_stack_var();
118*59d799daSIngo Weinhold tmp1   = M_get_stack_var();
119*59d799daSIngo Weinhold tmp2   = M_get_stack_var();
120*59d799daSIngo Weinhold 
121*59d799daSIngo Weinhold m_apm_absolute_value(tmpN, aa);
122*59d799daSIngo Weinhold 
123*59d799daSIngo Weinhold /*
124*59d799daSIngo Weinhold     normalize the input number (make the exponent 0) so
125*59d799daSIngo Weinhold     the 'guess' below will not over/under flow on large
126*59d799daSIngo Weinhold     magnitude exponents.
127*59d799daSIngo Weinhold */
128*59d799daSIngo Weinhold 
129*59d799daSIngo Weinhold nexp = aa->m_apm_exponent;
130*59d799daSIngo Weinhold tmpN->m_apm_exponent -= nexp;
131*59d799daSIngo Weinhold 
132*59d799daSIngo Weinhold m_apm_to_string(sbuf, 15, tmpN);
133*59d799daSIngo Weinhold m_apm_set_double(guess, (1.0 / atof(sbuf)));
134*59d799daSIngo Weinhold 
135*59d799daSIngo Weinhold tolerance = places + 4;
136*59d799daSIngo Weinhold dplaces   = places + 16;
137*59d799daSIngo Weinhold bflag     = FALSE;
138*59d799daSIngo Weinhold 
139*59d799daSIngo Weinhold m_apm_negate(last_x, MM_Ten);
140*59d799daSIngo Weinhold 
141*59d799daSIngo Weinhold /*   Use the following iteration to calculate the reciprocal :
142*59d799daSIngo Weinhold 
143*59d799daSIngo Weinhold 
144*59d799daSIngo Weinhold          X     =  X  *  [ 2 - N * X ]
145*59d799daSIngo Weinhold           n+1
146*59d799daSIngo Weinhold */
147*59d799daSIngo Weinhold 
148*59d799daSIngo Weinhold ii = 0;
149*59d799daSIngo Weinhold 
150*59d799daSIngo Weinhold while (TRUE)
151*59d799daSIngo Weinhold   {
152*59d799daSIngo Weinhold    m_apm_multiply(tmp1, tmpN, guess);
153*59d799daSIngo Weinhold    m_apm_subtract(tmp2, MM_Two, tmp1);
154*59d799daSIngo Weinhold    m_apm_multiply(tmp1, tmp2, guess);
155*59d799daSIngo Weinhold 
156*59d799daSIngo Weinhold    if (bflag)
157*59d799daSIngo Weinhold      break;
158*59d799daSIngo Weinhold 
159*59d799daSIngo Weinhold    m_apm_round(guess, dplaces, tmp1);
160*59d799daSIngo Weinhold 
161*59d799daSIngo Weinhold    /* force at least 2 iterations so 'last_x' has valid data */
162*59d799daSIngo Weinhold 
163*59d799daSIngo Weinhold    if (ii != 0)
164*59d799daSIngo Weinhold      {
165*59d799daSIngo Weinhold       m_apm_subtract(tmp2, guess, last_x);
166*59d799daSIngo Weinhold 
167*59d799daSIngo Weinhold       if (tmp2->m_apm_sign == 0)
168*59d799daSIngo Weinhold         break;
169*59d799daSIngo Weinhold 
170*59d799daSIngo Weinhold       /*
171*59d799daSIngo Weinhold        *   if we are within a factor of 4 on the error term,
172*59d799daSIngo Weinhold        *   we will be accurate enough after the *next* iteration
173*59d799daSIngo Weinhold        *   is complete.
174*59d799daSIngo Weinhold        */
175*59d799daSIngo Weinhold 
176*59d799daSIngo Weinhold       if ((-4 * tmp2->m_apm_exponent) > tolerance)
177*59d799daSIngo Weinhold         bflag = TRUE;
178*59d799daSIngo Weinhold      }
179*59d799daSIngo Weinhold 
180*59d799daSIngo Weinhold    m_apm_copy(last_x, guess);
181*59d799daSIngo Weinhold    ii++;
182*59d799daSIngo Weinhold   }
183*59d799daSIngo Weinhold 
184*59d799daSIngo Weinhold m_apm_round(rr, places, tmp1);
185*59d799daSIngo Weinhold rr->m_apm_exponent -= nexp;
186*59d799daSIngo Weinhold rr->m_apm_sign = aa->m_apm_sign;
187*59d799daSIngo Weinhold M_restore_stack(5);
188*59d799daSIngo Weinhold }
189*59d799daSIngo Weinhold /****************************************************************************/
190