root/trunk/thune/bignum.c

Revision 371, 7.8 kB (checked in by krobillard, 21 months ago)

Thune - Added bignum_mul().

Line 
1/*============================================================================
2    Urlan BigNums
3    Copyright (C) 2007  Karl Robillard
4
5    This library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9
10    This library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14
15    You should have received a copy of the GNU Lesser General Public
16    License along with this library; if not, write to the Free Software
17    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18===========================================================================*/
19
20/**
21  \file bignum.c
22  64.48 fixed point numbers appropriate for storage in UCells.
23*/
24
25
26#include <math.h>
27#include "urlan.h"
28#include "bignum.h"
29
30
31// Word layout:  [f0 f1 f2 i0 i1 i2 i3]
32
33typedef uint16_t    Limb;
34#define LIMBS       7
35#define IPOS        3
36#define BIG(c)      ((Limb*) c) + 1
37#define isNeg(b)    (b[LIMBS - 1] & 0x8000)
38
39
40#define OPT_32  1
41#ifdef OPT_32
42typedef struct
43{
44    uint8_t  type;
45    uint8_t  flags;
46    uint16_t u16;
47    uint32_t u32[ 3 ];
48}
49UCellBigNum;
50
51#define BIGC(c)     (UCellBigNum*) c
52#endif
53
54
55void bignum_zero( UCell* cell )
56{
57#ifdef OPT_32
58    UCellBigNum* bc = BIGC(cell);
59    bc->u16 = 0;
60    bc->u32[0] = bc->u32[1] = bc->u32[2] = 0;
61#else
62    Limb* bn  = BIG(cell);
63    Limb* end = bn + LIMBS;
64    while( bn != end )
65        *bn++ = 0;
66#endif
67}
68
69
70void bignum_seti( UCell* cell, int n )
71{
72    Limb* b = BIG(cell);
73
74    *b++ = 0;
75    *b++ = 0;
76    *b++ = 0;
77    *b++ = (Limb) (n & 0xffff);
78    *b++ = (Limb) ((n >> 16) & 0xffff);
79    if( n < 0 )
80    {
81        *b++ = 0xffff;
82        *b   = 0xffff;
83    }
84    else
85    {
86        *b++ = 0;
87        *b   = 0;
88    }
89}
90
91
92void bignum_setl( UCell* cell, int64_t n )
93{
94    Limb* b = BIG(cell);
95
96    *b++ = 0;
97    *b++ = 0;
98    *b++ = 0;
99    *b++ = (Limb) (n & 0xffff);
100    *b++ = (Limb) ((n >> 16) & 0xffff);
101    *b++ = (Limb) ((n >> 32) & 0xffff);
102    *b   = (Limb) ((n >> 48) & 0xffff);
103}
104
105
106void bignum_setd( UCell* cell, double n )
107{
108    const double lm1 = 65536.0;
109    const double lm2 = 65536.0 * 65536.0;
110    const double lm3 = 65536.0 * 65536.0 * 65536.0;
111    double d = (n < 0.0) ? -n : n;
112    double e = floor( d / lm3 );
113
114    if( e < 32767.0 )
115    {
116        Limb* bn = BIG(cell);
117
118        bn[6] = (Limb)  e;          d -= bn[6] * lm3;
119        bn[5] = (Limb) (d / lm2);   d -= bn[5] * lm2;
120        bn[4] = (Limb) (d / lm1);   d -= bn[4] * lm1;
121        bn[3] = (Limb)  d;          d -= bn[3];
122        bn[2] = (Limb) (d * lm1);   d -= bn[2] / lm1;
123        bn[1] = (Limb) (d * lm2);   d -= bn[1] / lm2;
124        bn[0] = (Limb) (d * lm3);
125
126        if( n < 0.0 )
127            bignum_negate( cell, cell );
128    }
129    else
130    {
131        // Number too large.
132        bignum_zero( cell );
133    }
134}
135
136
137int64_t bignum_l( const UCell* cell )
138{
139    uint64_t n;
140    Limb* b = BIG(cell) + IPOS;
141    n = b[0] + (((uint64_t) b[1]) << 16)
142             + (((uint64_t) b[2]) << 32)
143             + (((uint64_t) b[3]) << 48);
144    return (int64_t) n;
145}
146
147
148double bignum_d( const UCell* cell )
149{
150    double d = 0.0;
151    double e = 1.0 / (65536.0 * 65536.0 * 65536.0);
152    const Limb* b = BIG(cell);
153    const Limb* end;
154
155    if( isNeg(b) )
156    {
157        UCell pos;
158
159        bignum_negate( cell, &pos );
160
161        b = BIG(&pos);
162        end = b + LIMBS;
163        while( b != end )
164        {
165            d += e * *b++;
166            e *= 65536.0;
167        }
168        return -d;
169    }
170
171    end = b + LIMBS;
172    while( b != end )
173    {
174        d += e * *b++;
175        e *= 65536.0;
176    }
177    return d;
178}
179
180
181int bignum_equal( const UCell* a, const UCell* b )
182{
183#ifdef OPT_32
184    const UCellBigNum* ca = BIGC(a);
185    const UCellBigNum* cb = BIGC(b);
186
187    if( ca->u32[1] != cb->u32[1] )
188        return 0;
189    if( ca->u32[2] != cb->u32[2] )
190        return 0;
191    if( ca->u32[0] != cb->u32[0] )
192        return 0;
193    if( ca->u16 != cb->u16 )
194        return 0;
195#else
196    const Limb* ba = BIG(a);
197    const Limb* bb = BIG(b);
198    int i;
199    for( i = 0; i < LIMBS; ++i )
200    {
201        if( ba[i] != bb[i] )
202            return 0;
203    }
204#endif
205    return 1;
206}
207
208
209/**
210  Returns 1, 0, or -1 if a is greater than, equal to, or less than b.
211*/
212int bignum_cmp( const UCell* a, const UCell* b )
213{
214    const Limb* ba = BIG(a);
215    const Limb* bb = BIG(b);
216    int i;
217
218    if( isNeg(ba) )
219    {
220        if( ! isNeg(bb) )
221            return -1;
222    }
223    else if( isNeg(bb) )
224    {
225        return 1;
226    }
227
228    for( i = LIMBS - 1; i > -1; --i )
229    {
230        if( ba[i] > bb[i] )
231            return 1;
232        if( ba[i] < bb[i] )
233            return -1;
234    }
235    return 0;
236}
237
238
239/**
240  Cell and result may be the same.
241*/
242void bignum_negate( const UCell* cell, UCell* result )
243{
244    int i;
245    uint32_t tmp;
246    uint32_t carry = 1;
247    const Limb* bn = BIG(cell);
248    Limb* res = BIG(result);
249
250    for( i = 0; carry && (i < LIMBS); ++i )
251    {
252        tmp = ((uint16_t) ~bn[i]) + carry;
253        carry = tmp >> 16;
254        res[i] = (Limb) (tmp & 0xffff);
255    }
256
257    while( i < LIMBS )
258    {
259        res[i] = ~bn[i];
260        ++i;
261    }
262}
263
264
265/**
266  A, b, & result may point to the same cell.
267*/
268void bignum_add( const UCell* a, const UCell* b, UCell* result )
269{
270    int i;
271    uint32_t tmp;
272    uint32_t carry = 0;
273    const Limb* ba = BIG(a);
274    const Limb* bb = BIG(b);
275    Limb* res = BIG(result);
276
277    for( i = 0; i < LIMBS; ++i )
278    {
279        tmp = ba[i] + bb[i] + carry;
280        carry = tmp >> 16;
281        res[i] = (Limb) (tmp & 0xffff);
282    }
283}
284
285
286/**
287  A, b, & result may point to the same cell.
288*/
289void bignum_sub( const UCell* a, const UCell* b, UCell* result )
290{
291    UCell neg;
292    bignum_negate( b, &neg );
293    bignum_add( a, &neg, result );
294}
295
296
297/**
298  A, b, & result may point to the same cell.
299*/
300void bignum_mul( const UCell* a, const UCell* b, UCell* result )
301{
302    UCell posA, posB;
303    Limb prod[(LIMBS * 2) + 1];
304    Limb* res;
305    const Limb* ba = BIG(a);
306    const Limb* bb = BIG(b);
307    char sign = 0;
308    uint32_t mul, tmp, carry;
309    int i, j;
310
311    //if( a == b )
312    //    return square;
313
314    if( isNeg(ba) )
315    {
316        sign = 1;
317        bignum_negate( a, &posA );
318        ba = BIG(&posA);
319    }
320    if( isNeg(bb) )
321    {
322        sign ^= 1;
323        bignum_negate( b, &posB );
324        bb = BIG(&posB);
325    }
326
327    // The high (overflow) limbs need not be initialized.
328    for( i = 0; i < (LIMBS + 3); ++i )
329        prod[i] = 0;
330
331    res = prod;
332    for( i = 0; i < LIMBS; ++i )
333    {
334        mul = ba[i];
335        if( mul )
336        {
337            carry = 0;
338            for( j = 0; j < LIMBS; ++j )
339            {
340                tmp = mul * bb[j] + carry;
341                res[j] += tmp & 0xffff;
342                carry = tmp >> 16;
343            }
344            res[j] += carry;
345        }
346        ++res;
347    }
348
349    res = BIG(result);
350    for( i = 0; i < LIMBS; ++i )
351        res[i] = prod[i + 3];
352    if( sign )
353        bignum_negate( result, result );
354}
355
356
357#ifdef UNIT_TEST
358// gcc -DUNIT_TEST bignum.c -lm
359
360#include <stdio.h>
361
362int main()
363{
364    UCell ca, cb, cc;
365
366#define A   &ca
367#define B   &cb
368#define C   &cc
369
370#define print_d(op) \
371    printf( "%f %c %f = %f\n", bignum_d(A), op, bignum_d(B), bignum_d(C) )
372
373#define print_l(op) \
374    printf( "%ld %c %ld = %ld\n", bignum_l(A), op, bignum_l(B), bignum_l(C) )
375
376    bignum_zero( A );
377    bignum_seti( B, 2 );
378    bignum_add( A, B, C );
379    print_d( '+' );
380
381    bignum_seti( A, 1 );
382    bignum_seti( B, -3 );
383    bignum_add( A, B, C );
384    print_d( '+' );
385
386    bignum_setd( A, 500432.887 );
387    bignum_seti( B, -3 );
388    bignum_sub( A, B, C );
389    print_d( '-' );
390    print_l( '-' );
391
392    return 0;
393}
394#endif
395
396
397/*EOF*/
Note: See TracBrowser for help on using the browser.