1*3d8817e4Smiod /* flonum_mult.c - multiply two flonums
2*3d8817e4Smiod Copyright 1987, 1990, 1991, 1992, 1995, 2000, 2002, 2003
3*3d8817e4Smiod Free Software Foundation, Inc.
4*3d8817e4Smiod
5*3d8817e4Smiod This file is part of Gas, the GNU Assembler.
6*3d8817e4Smiod
7*3d8817e4Smiod The GNU assembler is distributed in the hope that it will be
8*3d8817e4Smiod useful, but WITHOUT ANY WARRANTY. No author or distributor
9*3d8817e4Smiod accepts responsibility to anyone for the consequences of using it
10*3d8817e4Smiod or for whether it serves any particular purpose or works at all,
11*3d8817e4Smiod unless he says so in writing. Refer to the GNU Assembler General
12*3d8817e4Smiod Public License for full details.
13*3d8817e4Smiod
14*3d8817e4Smiod Everyone is granted permission to copy, modify and redistribute
15*3d8817e4Smiod the GNU Assembler, but only under the conditions described in the
16*3d8817e4Smiod GNU Assembler General Public License. A copy of this license is
17*3d8817e4Smiod supposed to have been given to you along with the GNU Assembler
18*3d8817e4Smiod so you can know your rights and responsibilities. It should be
19*3d8817e4Smiod in a file named COPYING. Among other things, the copyright
20*3d8817e4Smiod notice and this notice must be preserved on all copies. */
21*3d8817e4Smiod
22*3d8817e4Smiod #include "ansidecl.h"
23*3d8817e4Smiod #include "flonum.h"
24*3d8817e4Smiod
25*3d8817e4Smiod /* plan for a . b => p(roduct)
26*3d8817e4Smiod
27*3d8817e4Smiod +-------+-------+-/ /-+-------+-------+
28*3d8817e4Smiod | a | a | ... | a | a |
29*3d8817e4Smiod | A | A-1 | | 1 | 0 |
30*3d8817e4Smiod +-------+-------+-/ /-+-------+-------+
31*3d8817e4Smiod
32*3d8817e4Smiod +-------+-------+-/ /-+-------+-------+
33*3d8817e4Smiod | b | b | ... | b | b |
34*3d8817e4Smiod | B | B-1 | | 1 | 0 |
35*3d8817e4Smiod +-------+-------+-/ /-+-------+-------+
36*3d8817e4Smiod
37*3d8817e4Smiod +-------+-------+-/ /-+-------+-/ /-+-------+-------+
38*3d8817e4Smiod | p | p | ... | p | ... | p | p |
39*3d8817e4Smiod | A+B+1| A+B | | N | | 1 | 0 |
40*3d8817e4Smiod +-------+-------+-/ /-+-------+-/ /-+-------+-------+
41*3d8817e4Smiod
42*3d8817e4Smiod /^\
43*3d8817e4Smiod (carry) a .b ... | ... a .b a .b
44*3d8817e4Smiod A B | 0 1 0 0
45*3d8817e4Smiod |
46*3d8817e4Smiod ... | ... a .b
47*3d8817e4Smiod | 1 0
48*3d8817e4Smiod |
49*3d8817e4Smiod | ...
50*3d8817e4Smiod |
51*3d8817e4Smiod |
52*3d8817e4Smiod |
53*3d8817e4Smiod | ___
54*3d8817e4Smiod | \
55*3d8817e4Smiod +----- P = > a .b
56*3d8817e4Smiod N /__ i j
57*3d8817e4Smiod
58*3d8817e4Smiod N = 0 ... A+B
59*3d8817e4Smiod
60*3d8817e4Smiod for all i,j where i+j=N
61*3d8817e4Smiod [i,j integers > 0]
62*3d8817e4Smiod
63*3d8817e4Smiod a[], b[], p[] may not intersect.
64*3d8817e4Smiod Zero length factors signify 0 significant bits: treat as 0.0.
65*3d8817e4Smiod 0.0 factors do the right thing.
66*3d8817e4Smiod Zero length product OK.
67*3d8817e4Smiod
68*3d8817e4Smiod I chose the ForTran accent "foo[bar]" instead of the C accent "*garply"
69*3d8817e4Smiod because I felt the ForTran way was more intuitive. The C way would
70*3d8817e4Smiod probably yield better code on most C compilers. Dean Elsner.
71*3d8817e4Smiod (C style also gives deeper insight [to me] ... oh well ...) */
72*3d8817e4Smiod
73*3d8817e4Smiod void
flonum_multip(const FLONUM_TYPE * a,const FLONUM_TYPE * b,FLONUM_TYPE * product)74*3d8817e4Smiod flonum_multip (const FLONUM_TYPE *a, const FLONUM_TYPE *b,
75*3d8817e4Smiod FLONUM_TYPE *product)
76*3d8817e4Smiod {
77*3d8817e4Smiod int size_of_a; /* 0 origin */
78*3d8817e4Smiod int size_of_b; /* 0 origin */
79*3d8817e4Smiod int size_of_product; /* 0 origin */
80*3d8817e4Smiod int size_of_sum; /* 0 origin */
81*3d8817e4Smiod int extra_product_positions; /* 1 origin */
82*3d8817e4Smiod unsigned long work;
83*3d8817e4Smiod unsigned long carry;
84*3d8817e4Smiod long exponent;
85*3d8817e4Smiod LITTLENUM_TYPE *q;
86*3d8817e4Smiod long significant; /* TRUE when we emit a non-0 littlenum */
87*3d8817e4Smiod /* ForTran accent follows. */
88*3d8817e4Smiod int P; /* Scan product low-order -> high. */
89*3d8817e4Smiod int N; /* As in sum above. */
90*3d8817e4Smiod int A; /* Which [] of a? */
91*3d8817e4Smiod int B; /* Which [] of b? */
92*3d8817e4Smiod
93*3d8817e4Smiod if ((a->sign != '-' && a->sign != '+')
94*3d8817e4Smiod || (b->sign != '-' && b->sign != '+'))
95*3d8817e4Smiod {
96*3d8817e4Smiod /* Got to fail somehow. Any suggestions? */
97*3d8817e4Smiod product->sign = 0;
98*3d8817e4Smiod return;
99*3d8817e4Smiod }
100*3d8817e4Smiod product->sign = (a->sign == b->sign) ? '+' : '-';
101*3d8817e4Smiod size_of_a = a->leader - a->low;
102*3d8817e4Smiod size_of_b = b->leader - b->low;
103*3d8817e4Smiod exponent = a->exponent + b->exponent;
104*3d8817e4Smiod size_of_product = product->high - product->low;
105*3d8817e4Smiod size_of_sum = size_of_a + size_of_b;
106*3d8817e4Smiod extra_product_positions = size_of_product - size_of_sum;
107*3d8817e4Smiod if (extra_product_positions < 0)
108*3d8817e4Smiod {
109*3d8817e4Smiod P = extra_product_positions; /* P < 0 */
110*3d8817e4Smiod exponent -= extra_product_positions; /* Increases exponent. */
111*3d8817e4Smiod }
112*3d8817e4Smiod else
113*3d8817e4Smiod {
114*3d8817e4Smiod P = 0;
115*3d8817e4Smiod }
116*3d8817e4Smiod carry = 0;
117*3d8817e4Smiod significant = 0;
118*3d8817e4Smiod for (N = 0; N <= size_of_sum; N++)
119*3d8817e4Smiod {
120*3d8817e4Smiod work = carry;
121*3d8817e4Smiod carry = 0;
122*3d8817e4Smiod for (A = 0; A <= N; A++)
123*3d8817e4Smiod {
124*3d8817e4Smiod B = N - A;
125*3d8817e4Smiod if (A <= size_of_a && B <= size_of_b && B >= 0)
126*3d8817e4Smiod {
127*3d8817e4Smiod #ifdef TRACE
128*3d8817e4Smiod printf ("a:low[%d.]=%04x b:low[%d.]=%04x work_before=%08x\n",
129*3d8817e4Smiod A, a->low[A], B, b->low[B], work);
130*3d8817e4Smiod #endif
131*3d8817e4Smiod /* Watch out for sign extension! Without the casts, on
132*3d8817e4Smiod the DEC Alpha, the multiplication result is *signed*
133*3d8817e4Smiod int, which gets sign-extended to convert to the
134*3d8817e4Smiod unsigned long! */
135*3d8817e4Smiod work += (unsigned long) a->low[A] * (unsigned long) b->low[B];
136*3d8817e4Smiod carry += work >> LITTLENUM_NUMBER_OF_BITS;
137*3d8817e4Smiod work &= LITTLENUM_MASK;
138*3d8817e4Smiod #ifdef TRACE
139*3d8817e4Smiod printf ("work=%08x carry=%04x\n", work, carry);
140*3d8817e4Smiod #endif
141*3d8817e4Smiod }
142*3d8817e4Smiod }
143*3d8817e4Smiod significant |= work;
144*3d8817e4Smiod if (significant || P < 0)
145*3d8817e4Smiod {
146*3d8817e4Smiod if (P >= 0)
147*3d8817e4Smiod {
148*3d8817e4Smiod product->low[P] = work;
149*3d8817e4Smiod #ifdef TRACE
150*3d8817e4Smiod printf ("P=%d. work[p]:=%04x\n", P, work);
151*3d8817e4Smiod #endif
152*3d8817e4Smiod }
153*3d8817e4Smiod P++;
154*3d8817e4Smiod }
155*3d8817e4Smiod else
156*3d8817e4Smiod {
157*3d8817e4Smiod extra_product_positions++;
158*3d8817e4Smiod exponent++;
159*3d8817e4Smiod }
160*3d8817e4Smiod }
161*3d8817e4Smiod /* [P]-> position # size_of_sum + 1.
162*3d8817e4Smiod This is where 'carry' should go. */
163*3d8817e4Smiod #ifdef TRACE
164*3d8817e4Smiod printf ("final carry =%04x\n", carry);
165*3d8817e4Smiod #endif
166*3d8817e4Smiod if (carry)
167*3d8817e4Smiod {
168*3d8817e4Smiod if (extra_product_positions > 0)
169*3d8817e4Smiod product->low[P] = carry;
170*3d8817e4Smiod else
171*3d8817e4Smiod {
172*3d8817e4Smiod /* No room at high order for carry littlenum. */
173*3d8817e4Smiod /* Shift right 1 to make room for most significant littlenum. */
174*3d8817e4Smiod exponent++;
175*3d8817e4Smiod P--;
176*3d8817e4Smiod for (q = product->low + P; q >= product->low; q--)
177*3d8817e4Smiod {
178*3d8817e4Smiod work = *q;
179*3d8817e4Smiod *q = carry;
180*3d8817e4Smiod carry = work;
181*3d8817e4Smiod }
182*3d8817e4Smiod }
183*3d8817e4Smiod }
184*3d8817e4Smiod else
185*3d8817e4Smiod P--;
186*3d8817e4Smiod product->leader = product->low + P;
187*3d8817e4Smiod product->exponent = exponent;
188*3d8817e4Smiod }
189