fix conditionals (prevents GMP always being built)
[packages/integer-gmp.git] / cbits / float.c
1 /* -----------------------------------------------------------------------------
2 *
3 * (c) Lennart Augustsson
4 * (c) The GHC Team, 1998-2000
5 *
6 * Support for floating-point <-> gmp integer primitives
7 *
8 * ---------------------------------------------------------------------------*/
9
10 /* TODO: do we need PosixSource.h ? it lives in rts/ not public includes/ */
11 /* #include "PosixSource.h" */
12 #include "Rts.h"
13 #include "gmp.h"
14
15 #include <math.h>
16
17 /*
18 * Encoding and decoding Doubles. Code based on the HBC code
19 * (lib/fltcode.c).
20 */
21
22 #ifdef _SHORT_LIMB
23 #define SIZEOF_LIMB_T SIZEOF_UNSIGNED_INT
24 #else
25 #ifdef _LONG_LONG_LIMB
26 #define SIZEOF_LIMB_T SIZEOF_UNSIGNED_LONG_LONG
27 #else
28 #define SIZEOF_LIMB_T SIZEOF_UNSIGNED_LONG
29 #endif
30 #endif
31
32 #if SIZEOF_LIMB_T == 4
33 #define GMP_BASE 4294967296.0
34 #elif SIZEOF_LIMB_T == 8
35 #define GMP_BASE 18446744073709551616.0
36 #else
37 #error Cannot cope with SIZEOF_LIMB_T -- please add definition of GMP_BASE
38 #endif
39
40 #define DNBIGIT ((SIZEOF_DOUBLE+SIZEOF_LIMB_T-1)/SIZEOF_LIMB_T)
41 #define FNBIGIT ((SIZEOF_FLOAT +SIZEOF_LIMB_T-1)/SIZEOF_LIMB_T)
42
43 #if IEEE_FLOATING_POINT
44 #define MY_DMINEXP ((DBL_MIN_EXP) - (DBL_MANT_DIG) - 1)
45 /* DMINEXP is defined in values.h on Linux (for example) */
46 #define DHIGHBIT 0x00100000
47 #define DMSBIT 0x80000000
48
49 #define MY_FMINEXP ((FLT_MIN_EXP) - (FLT_MANT_DIG) - 1)
50 #define FHIGHBIT 0x00800000
51 #define FMSBIT 0x80000000
52 #endif
53
54 #if defined(WORDS_BIGENDIAN) || defined(FLOAT_WORDS_BIGENDIAN)
55 #define L 1
56 #define H 0
57 #else
58 #define L 0
59 #define H 1
60 #endif
61
62 #define __abs(a) (( (a) >= 0 ) ? (a) : (-(a)))
63
64 StgDouble
65 integer_cbits_encodeDouble (I_ size, StgByteArray ba, I_ e) /* result = s * 2^e */
66 {
67 StgDouble r;
68 const mp_limb_t *const arr = (const mp_limb_t *)ba;
69 I_ i;
70
71 /* Convert MP_INT to a double; knows a lot about internal rep! */
72 for(r = 0.0, i = __abs(size)-1; i >= 0; i--)
73 r = (r * GMP_BASE) + arr[i];
74
75 /* Now raise to the exponent */
76 if ( r != 0.0 ) /* Lennart suggests this avoids a bug in MIPS's ldexp */
77 r = ldexp(r, e);
78
79 /* sign is encoded in the size */
80 if (size < 0)
81 r = -r;
82
83 return r;
84 }
85
86 StgFloat
87 integer_cbits_encodeFloat (I_ size, StgByteArray ba, I_ e) /* result = s * 2^e */
88 {
89 StgFloat r;
90 const mp_limb_t *arr = (const mp_limb_t *)ba;
91 I_ i;
92
93 /* Convert MP_INT to a float; knows a lot about internal rep! */
94 for(r = 0.0, i = __abs(size)-1; i >= 0; i--)
95 r = (r * GMP_BASE) + arr[i];
96
97 /* Now raise to the exponent */
98 if ( r != 0.0 ) /* Lennart suggests this avoids a bug in MIPS's ldexp */
99 r = ldexp(r, e);
100
101 /* sign is encoded in the size */
102 if (size < 0)
103 r = -r;
104
105 return r;
106 }
107
108 /* This only supports IEEE floating point */
109
110 void
111 integer_cbits_decodeDouble (MP_INT *man, I_ *exp, StgDouble dbl)
112 {
113 /* Do some bit fiddling on IEEE */
114 unsigned int low, high; /* assuming 32 bit ints */
115 int sign, iexp;
116 union { double d; unsigned int i[2]; } u; /* assuming 32 bit ints, 64 bit double */
117
118 ASSERT(sizeof(unsigned int ) == 4 );
119 ASSERT(sizeof(dbl ) == SIZEOF_DOUBLE);
120 ASSERT(sizeof(man->_mp_d[0]) == SIZEOF_LIMB_T);
121 ASSERT(DNBIGIT*SIZEOF_LIMB_T >= SIZEOF_DOUBLE);
122
123 u.d = dbl; /* grab chunks of the double */
124 low = u.i[L];
125 high = u.i[H];
126
127 /* we know the MP_INT* passed in has size zero, so we realloc
128 no matter what.
129 */
130 man->_mp_alloc = DNBIGIT;
131
132 if (low == 0 && (high & ~DMSBIT) == 0) {
133 man->_mp_size = 0;
134 *exp = 0L;
135 } else {
136 man->_mp_size = DNBIGIT;
137 iexp = ((high >> 20) & 0x7ff) + MY_DMINEXP;
138 sign = high;
139
140 high &= DHIGHBIT-1;
141 if (iexp != MY_DMINEXP) /* don't add hidden bit to denorms */
142 high |= DHIGHBIT;
143 else {
144 iexp++;
145 /* A denorm, normalize the mantissa */
146 while (! (high & DHIGHBIT)) {
147 high <<= 1;
148 if (low & DMSBIT)
149 high++;
150 low <<= 1;
151 iexp--;
152 }
153 }
154 *exp = (I_) iexp;
155 #if DNBIGIT == 2
156 man->_mp_d[0] = (mp_limb_t)low;
157 man->_mp_d[1] = (mp_limb_t)high;
158 #else
159 #if DNBIGIT == 1
160 man->_mp_d[0] = ((mp_limb_t)high) << 32 | (mp_limb_t)low;
161 #else
162 #error Cannot cope with DNBIGIT
163 #endif
164 #endif
165 if (sign < 0)
166 man->_mp_size = -man->_mp_size;
167 }
168 }