xref: /openbsd-src/gnu/usr.bin/binutils-2.17/gas/flonum-mult.c (revision 3d8817e467ea46cf4772788d6804dd293abfb01a)
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