1 /* float.c floating-point constant support for the Netwide Assembler
3 * The Netwide Assembler is copyright (C) 1996 Simon Tatham and
4 * Julian Hall. All rights reserved. The software is
5 * redistributable under the licence given in the file "Licence"
6 * distributed in the NASM archive.
8 * initial version 13/ix/96 by Simon Tatham
21 #define MANT_WORDS 6 /* 64 bits + 32 for accuracy == 96 */
22 #define MANT_DIGITS 28 /* 29 digits don't fit in 96 bits */
25 * guaranteed top bit of from is set
26 * => we only have to worry about _one_ bit shift to the left
29 static int ieee_multiply(uint16_t *to
, uint16_t *from
)
31 uint32_t temp
[MANT_WORDS
* 2];
34 for (i
= 0; i
< MANT_WORDS
* 2; i
++)
37 for (i
= 0; i
< MANT_WORDS
; i
++)
38 for (j
= 0; j
< MANT_WORDS
; j
++) {
40 n
= (uint32_t)to
[i
] * (uint32_t)from
[j
];
41 temp
[i
+ j
] += n
>> 16;
42 temp
[i
+ j
+ 1] += n
& 0xFFFF;
45 for (i
= MANT_WORDS
* 2; --i
;) {
46 temp
[i
- 1] += temp
[i
] >> 16;
49 if (temp
[0] & 0x8000) {
50 for (i
= 0; i
< MANT_WORDS
; i
++)
51 to
[i
] = temp
[i
] & 0xFFFF;
54 for (i
= 0; i
< MANT_WORDS
; i
++)
55 to
[i
] = (temp
[i
] << 1) + !!(temp
[i
+ 1] & 0x8000);
60 static void ieee_flconvert(char *string
, uint16_t *mant
,
61 int32_t *exponent
, efunc error
)
63 char digits
[MANT_DIGITS
];
65 uint16_t mult
[MANT_WORDS
], bit
;
67 int32_t tenpwr
, twopwr
;
68 int extratwos
, started
, seendot
;
72 started
= seendot
= FALSE
;
73 while (*string
&& *string
!= 'E' && *string
!= 'e') {
79 "too many periods in floating-point constant");
82 } else if (*string
>= '0' && *string
<= '9') {
83 if (*string
== '0' && !started
) {
88 if (p
< digits
+ sizeof(digits
))
95 "floating-point constant: `%c' is invalid character",
102 string
++; /* eat the E */
103 tenpwr
+= atoi(string
);
107 * At this point, the memory interval [digits,p) contains a
108 * series of decimal digits zzzzzzz such that our number X
111 * X = 0.zzzzzzz * 10^tenpwr
115 for (m
= mant
; m
< mant
+ MANT_WORDS
; m
++)
121 while (m
< mant
+ MANT_WORDS
) {
123 while (p
> q
&& !p
[-1])
127 for (r
= p
; r
-- > q
;) {
138 *m
|= bit
, started
= TRUE
;
150 * At this point the `mant' array contains the first six
151 * fractional places of a base-2^16 real number, which when
152 * multiplied by 2^twopwr and 5^tenpwr gives X. So now we
153 * really do multiply by 5^tenpwr.
157 for (m
= mult
; m
< mult
+ MANT_WORDS
; m
++)
161 } else if (tenpwr
> 0) {
163 for (m
= mult
+ 1; m
< mult
+ MANT_WORDS
; m
++)
170 twopwr
+= extratwos
+ ieee_multiply(mant
, mult
);
171 extratwos
= extratwos
* 2 + ieee_multiply(mult
, mult
);
176 * Conversion is done. The elements of `mant' contain the first
177 * fractional places of a base-2^16 real number in [0.5,1)
178 * which we can multiply by 2^twopwr to get X. Or, of course,
185 * Shift a mantissa to the right by i (i < 16) bits.
187 static void ieee_shr(uint16_t *mant
, int i
)
192 for (j
= 0; j
< MANT_WORDS
; j
++) {
193 m
= (mant
[j
] << (16 - i
)) & 0xFFFF;
194 mant
[j
] = (mant
[j
] >> i
) | n
;
200 * Round a mantissa off after i words.
202 static int ieee_round(uint16_t *mant
, int i
)
204 if (mant
[i
] & 0x8000) {
208 } while (i
> 0 && !mant
[i
]);
209 return !i
&& !mant
[i
];
214 #define put(a,b) ( (*(a)=(b)), ((a)[1]=(b)>>8) )
216 static int to_double(char *str
, int32_t sign
, uint8_t *result
,
219 uint16_t mant
[MANT_WORDS
];
222 sign
= (sign
< 0 ? 0x8000L
: 0L);
224 ieee_flconvert(str
, mant
, &exponent
, error
);
225 if (mant
[0] & 0x8000) {
230 if (exponent
>= -1022 && exponent
<= 1024) {
237 if (mant
[0] & 0x20) /* did we scale up by one? */
238 ieee_shr(mant
, 1), exponent
++;
239 mant
[0] &= 0xF; /* remove leading one */
240 put(result
+ 6, (exponent
<< 4) | mant
[0] | sign
);
241 put(result
+ 4, mant
[1]);
242 put(result
+ 2, mant
[2]);
243 put(result
+ 0, mant
[3]);
244 } else if (exponent
< -1022 && exponent
>= -1074) {
248 int shift
= -(exponent
+ 1011);
249 int sh
= shift
% 16, wds
= shift
/ 16;
251 if (ieee_round(mant
, 4 - wds
)
252 || (sh
> 0 && (mant
[0] & (0x8000 >> (sh
- 1))))) {
258 put(result
+ 6, (wds
== 0 ? mant
[0] : 0) | sign
);
259 put(result
+ 4, (wds
<= 1 ? mant
[1 - wds
] : 0));
260 put(result
+ 2, (wds
<= 2 ? mant
[2 - wds
] : 0));
261 put(result
+ 0, (wds
<= 3 ? mant
[3 - wds
] : 0));
264 error(ERR_NONFATAL
, "overflow in floating-point constant");
267 memset(result
, 0, 8);
273 memset(result
, 0, 8);
275 return 1; /* success */
278 static int to_float(char *str
, int32_t sign
, uint8_t *result
,
281 uint16_t mant
[MANT_WORDS
];
284 sign
= (sign
< 0 ? 0x8000L
: 0L);
286 ieee_flconvert(str
, mant
, &exponent
, error
);
287 if (mant
[0] & 0x8000) {
292 if (exponent
>= -126 && exponent
<= 128) {
299 if (mant
[0] & 0x100) /* did we scale up by one? */
300 ieee_shr(mant
, 1), exponent
++;
301 mant
[0] &= 0x7F; /* remove leading one */
302 put(result
+ 2, (exponent
<< 7) | mant
[0] | sign
);
303 put(result
+ 0, mant
[1]);
304 } else if (exponent
< -126 && exponent
>= -149) {
308 int shift
= -(exponent
+ 118);
309 int sh
= shift
% 16, wds
= shift
/ 16;
311 if (ieee_round(mant
, 2 - wds
)
312 || (sh
> 0 && (mant
[0] & (0x8000 >> (sh
- 1))))) {
318 put(result
+ 2, (wds
== 0 ? mant
[0] : 0) | sign
);
319 put(result
+ 0, (wds
<= 1 ? mant
[1 - wds
] : 0));
322 error(ERR_NONFATAL
, "overflow in floating-point constant");
325 memset(result
, 0, 4);
328 memset(result
, 0, 4);
333 static int to_ldoub(char *str
, int32_t sign
, uint8_t *result
,
336 uint16_t mant
[MANT_WORDS
];
339 sign
= (sign
< 0 ? 0x8000L
: 0L);
341 ieee_flconvert(str
, mant
, &exponent
, error
);
342 if (mant
[0] & 0x8000) {
347 if (exponent
>= -16383 && exponent
<= 16384) {
352 if (ieee_round(mant
, 4)) /* did we scale up by one? */
353 ieee_shr(mant
, 1), mant
[0] |= 0x8000, exponent
++;
354 put(result
+ 8, exponent
| sign
);
355 put(result
+ 6, mant
[0]);
356 put(result
+ 4, mant
[1]);
357 put(result
+ 2, mant
[2]);
358 put(result
+ 0, mant
[3]);
359 } else if (exponent
< -16383 && exponent
>= -16446) {
363 int shift
= -(exponent
+ 16383);
364 int sh
= shift
% 16, wds
= shift
/ 16;
366 if (ieee_round(mant
, 4 - wds
)
367 || (sh
> 0 && (mant
[0] & (0x8000 >> (sh
- 1))))) {
373 put(result
+ 8, sign
);
374 put(result
+ 6, (wds
== 0 ? mant
[0] : 0));
375 put(result
+ 4, (wds
<= 1 ? mant
[1 - wds
] : 0));
376 put(result
+ 2, (wds
<= 2 ? mant
[2 - wds
] : 0));
377 put(result
+ 0, (wds
<= 3 ? mant
[3 - wds
] : 0));
380 error(ERR_NONFATAL
, "overflow in floating-point constant");
383 memset(result
, 0, 10);
389 memset(result
, 0, 10);
394 int float_const(char *number
, int32_t sign
, uint8_t *result
, int bytes
,
398 return to_float(number
, sign
, result
, error
);
400 return to_double(number
, sign
, result
, error
);
401 else if (bytes
== 10)
402 return to_ldoub(number
, sign
, result
, error
);
404 error(ERR_PANIC
, "strange value %d passed to float_const", bytes
);