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