diff options
author | H. Peter Anvin <hpa@zytor.com> | 2002-04-30 20:51:32 +0000 |
---|---|---|
committer | H. Peter Anvin <hpa@zytor.com> | 2002-04-30 20:51:32 +0000 |
commit | ea6e34db64c7da7cb885197316c6b5e7d048bdb9 (patch) | |
tree | 78e728348f8fe09e394a51c3617e6261de0f4001 /float.c | |
download | nasm-ea6e34db64c7da7cb885197316c6b5e7d048bdb9.tar.gz nasm-ea6e34db64c7da7cb885197316c6b5e7d048bdb9.tar.bz2 nasm-ea6e34db64c7da7cb885197316c6b5e7d048bdb9.zip |
NASM 0.91
Diffstat (limited to 'float.c')
-rw-r--r-- | float.c | 389 |
1 files changed, 389 insertions, 0 deletions
@@ -0,0 +1,389 @@ +/* float.c floating-point constant support for the Netwide Assembler + * + * The Netwide Assembler is copyright (C) 1996 Simon Tatham and + * Julian Hall. All rights reserved. The software is + * redistributable under the licence given in the file "Licence" + * distributed in the NASM archive. + * + * initial version 13/ix/96 by Simon Tatham + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "nasm.h" + +#define TRUE 1 +#define FALSE 0 + +#define MANT_WORDS 6 /* 64 bits + 32 for accuracy == 96 */ +#define MANT_DIGITS 28 /* 29 digits don't fit in 96 bits */ + +/* + * guaranteed top bit of from is set + * => we only have to worry about _one_ bit shift to the left + */ + +static int multiply(unsigned short *to, unsigned short *from) { + unsigned long temp[MANT_WORDS*2]; + int i, j; + + for (i=0; i<MANT_WORDS*2; i++) + temp[i] = 0; + + for (i=0; i<MANT_WORDS; i++) + for (j=0; j<MANT_WORDS; j++) { + unsigned long n; + n = (unsigned long)to[i] * (unsigned long)from[j]; + temp[i+j] += n >> 16; + temp[i+j+1] += n & 0xFFFF; + } + + for (i=MANT_WORDS*2; --i ;) { + temp[i-1] += temp[i] >> 16; + temp[i] &= 0xFFFF; + } + if (temp[0] & 0x8000) { + for (i=0; i<MANT_WORDS; i++) + to[i] = temp[i] & 0xFFFF; + return 0; + } else { + for (i=0; i<MANT_WORDS; i++) + to[i] = (temp[i] << 1) + !!(temp[i+1] & 0x8000); + return -1; + } +} + +static void flconvert(char *string, unsigned short *mant, long *exponent) { + char digits[MANT_DIGITS], *p, *q, *r; + unsigned short mult[MANT_WORDS], *m, bit; + long tenpwr, twopwr; + int extratwos, started, seendot; + + p = digits; + tenpwr = 0; + started = seendot = FALSE; + while (*string && *string != 'E' && *string != 'e') { + if (*string == '.') { + if (!seendot) + seendot = TRUE; + else { + fprintf(stderr, "too many periods!\n"); + return; + } + } else if (*string >= '0' && *string <= '9') { + if (*string == '0' && !started) { + if (seendot) + tenpwr--; + } else { + started = TRUE; + if (p < digits+sizeof(digits)) + *p++ = *string - '0'; + if (!seendot) + tenpwr++; + } + } else { + fprintf(stderr, "`%c' is invalid char\n", *string); + return; + } + string++; + } + if (*string) { + string++; /* eat the E */ + tenpwr += atoi(string); + } + + /* + * At this point, the memory interval [digits,p) contains a + * series of decimal digits zzzzzzz such that our number X + * satisfies + * + * X = 0.zzzzzzz * 10^tenpwr + */ + + bit = 0x8000; + for (m=mant; m<mant+MANT_WORDS; m++) + *m = 0; + m = mant; + q = digits; + started = FALSE; + twopwr = 0; + while (m < mant+MANT_WORDS) { + unsigned short carry = 0; + while (p > q && !p[-1]) + p--; + if (p <= q) + break; + for (r = p; r-- > q ;) { + int i; + + i = 2 * *r + carry; + if (i >= 10) + carry = 1, i -= 10; + else + carry = 0; + *r = i; + } + if (carry) + *m |= bit, started = TRUE; + if (started) { + if (bit == 1) + bit = 0x8000, m++; + else + bit >>= 1; + } else + twopwr--; + } + twopwr += tenpwr; + + /* + * At this point the `mant' array contains the first six + * fractional places of a base-2^16 real number, which when + * multiplied by 2^twopwr and 5^tenpwr gives X. So now we + * really do multiply by 5^tenpwr. + */ + + if (tenpwr < 0) { + for (m=mult; m<mult+MANT_WORDS; m++) + *m = 0xCCCC; + extratwos = -2; + tenpwr = -tenpwr; + } else if (tenpwr > 0) { + mult[0] = 0xA000; + for (m=mult+1; m<mult+MANT_WORDS; m++) + *m = 0; + extratwos = 3; + } else + extratwos = 0; + while (tenpwr) { + if (tenpwr & 1) + twopwr += extratwos + multiply (mant, mult); + extratwos = extratwos * 2 + multiply (mult, mult); + tenpwr >>= 1; + } + + /* + * Conversion is done. The elements of `mant' contain the first + * fractional places of a base-2^16 real number in [0.5,1) + * which we can multiply by 2^twopwr to get X. Or, of course, + * it contains zero. + */ + *exponent = twopwr; +} + +/* + * Shift a mantissa to the right by i (i < 16) bits. + */ +static void shr(unsigned short *mant, int i) { + unsigned short n = 0, m; + int j; + + for (j=0; j<MANT_WORDS; j++) { + m = (mant[j] << (16-i)) & 0xFFFF; + mant[j] = (mant[j] >> i) | n; + n = m; + } +} + +/* + * Round a mantissa off after i words. + */ +static int round(unsigned short *mant, int i) { + if (mant[i] & 0x8000) { + do { + ++mant[--i]; + mant[i] &= 0xFFFF; + } while (i > 0 && !mant[i]); + return !i && !mant[i]; + } + return 0; +} + +#define put(a,b) ( (*(a)=(b)), ((a)[1]=(b)>>8) ) + +static int to_double(char *str, long sign, unsigned char *result, + efunc error) { + unsigned short mant[MANT_WORDS]; + long exponent; + + sign = (sign < 0 ? 0x8000L : 0L); + + flconvert (str, mant, &exponent); + if (mant[0] & 0x8000) { + /* + * Non-zero. + */ + exponent--; + if (exponent >= -1022 && exponent <= 1024) { + /* + * Normalised. + */ + exponent += 1023; + shr(mant, 11); + round(mant, 4); + if (mant[0] & 0x20) /* did we scale up by one? */ + shr(mant, 1), exponent++; + mant[0] &= 0xF; /* remove leading one */ + put(result+6,(exponent << 4) | mant[0] | sign); + put(result+4,mant[1]); + put(result+2,mant[2]); + put(result+0,mant[3]); + } else if (exponent < -1022 && exponent >= -1074) { + /* + * Denormal. + */ + int shift = -(exponent+1011); + int sh = shift % 16, wds = shift / 16; + shr(mant, sh); + if (round(mant, 4-wds) || (sh>0 && (mant[0]&(0x8000>>(sh-1))))) { + shr(mant, 1); + if (sh==0) + mant[0] |= 0x8000; + exponent++; + } + put(result+6,(wds == 0 ? mant[0] : 0) | sign); + put(result+4,(wds <= 1 ? mant[1-wds] : 0)); + put(result+2,(wds <= 2 ? mant[2-wds] : 0)); + put(result+0,(wds <= 3 ? mant[3-wds] : 0)); + } else { + if (exponent > 0) { + error(ERR_NONFATAL, "overflow in floating-point constant"); + return 0; + } else + memset (result, 0, 8); + } + } else { + /* + * Zero. + */ + memset (result, 0, 8); + } + return 1; /* success */ +} + +static int to_float(char *str, long sign, unsigned char *result, + efunc error) { + unsigned short mant[MANT_WORDS]; + long exponent; + + sign = (sign < 0 ? 0x8000L : 0L); + + flconvert (str, mant, &exponent); + if (mant[0] & 0x8000) { + /* + * Non-zero. + */ + exponent--; + if (exponent >= -126 && exponent <= 128) { + /* + * Normalised. + */ + exponent += 127; + shr(mant, 8); + round(mant, 2); + if (mant[0] & 0x100) /* did we scale up by one? */ + shr(mant, 1), exponent++; + mant[0] &= 0x7F; /* remove leading one */ + put(result+2,(exponent << 7) | mant[0] | sign); + put(result+0,mant[1]); + } else if (exponent < -126 && exponent >= -149) { + /* + * Denormal. + */ + int shift = -(exponent+118); + int sh = shift % 16, wds = shift / 16; + shr(mant, sh); + if (round(mant, 2-wds) || (sh>0 && (mant[0]&(0x8000>>(sh-1))))) { + shr(mant, 1); + if (sh==0) + mant[0] |= 0x8000; + exponent++; + } + put(result+2,(wds == 0 ? mant[0] : 0) | sign); + put(result+0,(wds <= 1 ? mant[1-wds] : 0)); + } else { + if (exponent > 0) { + error(ERR_NONFATAL, "overflow in floating-point constant"); + return 0; + } else + memset (result, 0, 4); + } + } else { + memset (result, 0, 4); + } + return 1; +} + +static int to_ldoub(char *str, long sign, unsigned char *result, + efunc error) { + unsigned short mant[MANT_WORDS]; + long exponent; + + sign = (sign < 0 ? 0x8000L : 0L); + + flconvert (str, mant, &exponent); + if (mant[0] & 0x8000) { + /* + * Non-zero. + */ + exponent--; + if (exponent >= -16383 && exponent <= 16384) { + /* + * Normalised. + */ + exponent += 16383; + if (round(mant, 4)) /* did we scale up by one? */ + shr(mant, 1), mant[0] |= 0x8000, exponent++; + put(result+8,exponent | sign); + put(result+6,mant[0]); + put(result+4,mant[1]); + put(result+2,mant[2]); + put(result+0,mant[3]); + } else if (exponent < -16383 && exponent >= -16446) { + /* + * Denormal. + */ + int shift = -(exponent+16383); + int sh = shift % 16, wds = shift / 16; + shr(mant, sh); + if (round(mant, 4-wds) || (sh>0 && (mant[0]&(0x8000>>(sh-1))))) { + shr(mant, 1); + if (sh==0) + mant[0] |= 0x8000; + exponent++; + } + put(result+8,sign); + put(result+6,(wds == 0 ? mant[0] : 0)); + put(result+4,(wds <= 1 ? mant[1-wds] : 0)); + put(result+2,(wds <= 2 ? mant[2-wds] : 0)); + put(result+0,(wds <= 3 ? mant[3-wds] : 0)); + } else { + if (exponent > 0) { + error(ERR_NONFATAL, "overflow in floating-point constant"); + return 0; + } else + memset (result, 0, 10); + } + } else { + /* + * Zero. + */ + memset (result, 0, 10); + } + return 1; +} + +int float_const (char *number, long sign, unsigned char *result, int bytes, + efunc error) { + if (bytes == 4) + return to_float (number, sign, result, error); + else if (bytes == 8) + return to_double (number, sign, result, error); + else if (bytes == 10) + return to_ldoub (number, sign, result, error); + else { + error(ERR_PANIC, "strange value %d passed to float_const", bytes); + return 0; + } +} |