/* (c) copyright 1989 by the Vrije Universiteit, Amsterdam, The Netherlands. See the copyright notice in the ACK home directory, in the file "Copyright". */ /* $Id: ext_comp.c,v 1.1.1.1 2005/04/21 14:56:05 beng Exp $ */ /* extended precision arithmetic for the strtod() and cvt() routines */ /* This may require some more work when long doubles get bigger than 8 bytes. In this case, these routines may become obsolete. ??? */ #include "ext_fmt.h" #include #include #include static int b64_add(struct mantissa *e1, struct mantissa *e2); static b64_sft(struct mantissa *e1, int n); static mul_ext(struct EXTEND *e1, struct EXTEND *e2, struct EXTEND *e3) { /* Multiply the extended numbers e1 and e2, and put the result in e3. */ register int i,j; /* loop control */ unsigned short mp[4]; unsigned short mc[4]; unsigned short result[8]; /* result */ register unsigned short *pres; /* first save the sign (XOR) */ e3->sign = e1->sign ^ e2->sign; /* compute new exponent */ e3->exp = e1->exp + e2->exp + 1; /* check for overflow/underflow ??? */ /* 128 bit multiply of mantissas */ /* assign unknown long formats */ /* to known unsigned word formats */ mp[0] = e1->m1 >> 16; mp[1] = (unsigned short) e1->m1; mp[2] = e1->m2 >> 16; mp[3] = (unsigned short) e1->m2; mc[0] = e2->m1 >> 16; mc[1] = (unsigned short) e2->m1; mc[2] = e2->m2 >> 16; mc[3] = (unsigned short) e2->m2; for (i = 8; i--;) { result[i] = 0; } /* * fill registers with their components */ for(i=4, pres = &result[4];i--;pres--) if (mp[i]) { unsigned short k = 0; unsigned long mpi = mp[i]; for(j=4;j--;) { unsigned long tmp = (unsigned long)pres[j] + k; if (mc[j]) tmp += mpi * mc[j]; pres[j] = tmp; k = tmp >> 16; } pres[-1] = k; } if (! (result[0] & 0x8000)) { e3->exp--; for (i = 0; i <= 3; i++) { result[i] <<= 1; if (result[i+1]&0x8000) result[i] |= 1; } result[4] <<= 1; } /* * combine the registers to a total */ e3->m1 = ((unsigned long)(result[0]) << 16) + result[1]; e3->m2 = ((unsigned long)(result[2]) << 16) + result[3]; if (result[4] & 0x8000) { if (++e3->m2 == 0) { if (++e3->m1 == 0) { e3->m1 = 0x80000000; e3->exp++; } } } } static add_ext(struct EXTEND *e1, struct EXTEND *e2, struct EXTEND *e3) { /* Add two extended numbers e1 and e2, and put the result in e3 */ struct EXTEND ce2; int diff; if ((e2->m1 | e2->m2) == 0L) { *e3 = *e1; return; } if ((e1->m1 | e1->m2) == 0L) { *e3 = *e2; return; } ce2 = *e2; *e3 = *e1; e1 = &ce2; /* adjust mantissas to equal power */ diff = e3->exp - e1->exp; if (diff < 0) { diff = -diff; e3->exp += diff; b64_sft(&(e3->mantissa), diff); } else if (diff > 0) { e1->exp += diff; b64_sft(&(e1->mantissa), diff); } if (e1->sign != e3->sign) { /* e3 + e1 = e3 - (-e1) */ if (e1->m1 > e3->m1 || (e1->m1 == e3->m1 && e1->m2 > e3->m2)) { /* abs(e1) > abs(e3) */ if (e3->m2 > e1->m2) { e1->m1 -= 1; /* carry in */ } e1->m1 -= e3->m1; e1->m2 -= e3->m2; *e3 = *e1; } else { if (e1->m2 > e3->m2) e3->m1 -= 1; /* carry in */ e3->m1 -= e1->m1; e3->m2 -= e1->m2; } } else { if (b64_add(&e3->mantissa,&e1->mantissa)) {/* addition carry */ b64_sft(&e3->mantissa,1);/* shift mantissa one bit RIGHT */ e3->m1 |= 0x80000000L; /* set max bit */ e3->exp++; /* increase the exponent */ } } if ((e3->m2 | e3->m1) != 0L) { /* normalize */ if (e3->m1 == 0L) { e3->m1 = e3->m2; e3->m2 = 0L; e3->exp -= 32; } if (!(e3->m1 & 0x80000000)) { unsigned long l = 0x40000000; int cnt = -1; while (! (l & e3->m1)) { l >>= 1; cnt--; } e3->exp += cnt; b64_sft(&(e3->mantissa), cnt); } } } static int cmp_ext(struct EXTEND *e1, struct EXTEND *e2) { struct EXTEND tmp; e2->sign = ! e2->sign; add_ext(e1, e2, &tmp); e2->sign = ! e2->sign; if (tmp.m1 == 0 && tmp.m2 == 0) return 0; if (tmp.sign) return -1; return 1; } static b64_sft(struct mantissa *e1, int n) { if (n > 0) { if (n > 63) { e1->l_32 = 0; e1->h_32 = 0; return; } if (n >= 32) { e1->l_32 = e1->h_32; e1->h_32 = 0; n -= 32; } if (n > 0) { e1->l_32 >>= n; if (e1->h_32 != 0) { e1->l_32 |= (e1->h_32 << (32 - n)); e1->h_32 >>= n; } } return; } n = -n; if (n > 0) { if (n > 63) { e1->l_32 = 0; e1->h_32 = 0; return; } if (n >= 32) { e1->h_32 = e1->l_32; e1->l_32 = 0; n -= 32; } if (n > 0) { e1->h_32 <<= n; if (e1->l_32 != 0) { e1->h_32 |= (e1->l_32 >> (32 - n)); e1->l_32 <<= n; } } } } static int b64_add(struct mantissa *e1, struct mantissa *e2) /* * pointers to 64 bit 'registers' */ { register int overflow; int carry; /* add higher pair of 32 bits */ overflow = ((unsigned long) 0xFFFFFFFF - e1->h_32 < e2->h_32); e1->h_32 += e2->h_32; /* add lower pair of 32 bits */ carry = ((unsigned long) 0xFFFFFFFF - e1->l_32 < e2->l_32); e1->l_32 += e2->l_32; if ((carry) && (++e1->h_32 == 0)) return(1); /* had a 64 bit overflow */ else return(overflow); /* return status from higher add */ } /* The following tables can be computed with the following bc(1) program: obase=16 scale=0 define t(x){ auto a, b, c a=2;b=1;c=2^32;n=1 while(asign = 0; e->exp = 0; e->m1 = e->m2 = 0; c = *s; switch(c) { case '-': e->sign = 1; case '+': s++; } while (c = *s++, isdigit(c) || (c == '.' && ! dotseen++)) { if (c == '.') continue; digitseen = 1; if (e->m1 <= (unsigned long)(0xFFFFFFFF)/10) { struct mantissa a1; a1 = e->mantissa; b64_sft(&(e->mantissa), -3); b64_sft(&a1, -1); b64_add(&(e->mantissa), &a1); a1.h_32 = 0; a1.l_32 = c - '0'; b64_add(&(e->mantissa), &a1); } else exp++; if (dotseen) exp--; } if (! digitseen) return; if (ss) *ss = (char *)s - 1; if (c == 'E' || c == 'e') { int exp1 = 0; int sign = 1; int exp_overflow = 0; switch(*s) { case '-': sign = -1; case '+': s++; } if (c = *s, isdigit(c)) { do { int tmp; exp1 = 10 * exp1 + (c - '0'); if ((tmp = sign * exp1 + exp) > MAX_EXP || tmp < -MAX_EXP) { exp_overflow = 1; } } while (c = *++s, isdigit(c)); if (ss) *ss = (char *)s; } exp += sign * exp1; if (exp_overflow) { exp = sign * MAX_EXP; if (e->m1 != 0 || e->m2 != 0) errno = ERANGE; } } if (e->m1 == 0 && e->m2 == 0) return; e->exp = 63; while (! (e->m1 & 0x80000000)) { b64_sft(&(e->mantissa),-1); e->exp--; } add_exponent(e, exp); } #include static ten_mult(struct EXTEND *e) { struct EXTEND e1 = *e; e1.exp++; e->exp += 3; add_ext(e, &e1, e); } #define NDIGITS 128 #define NSIGNIFICANT 19 char * _ext_str_cvt(struct EXTEND *e, int ndigit, int *decpt, int *sign, int ecvtflag) { /* Like cvt(), but for extended precision */ static char buf[NDIGITS+1]; struct EXTEND m; register char *p = buf; register char *pe; int findex = 0; if (ndigit < 0) ndigit = 0; if (ndigit > NDIGITS) ndigit = NDIGITS; pe = &buf[ndigit]; buf[0] = '\0'; *sign = 0; if (e->sign) { *sign = 1; e->sign = 0; } *decpt = 0; if (e->m1 != 0) { register struct EXTEND *pp = &big_ten_powers[1]; while(cmp_ext(e,pp) >= 0) { pp++; findex = pp - big_ten_powers; if (findex >= BTP) break; } pp--; findex = pp - big_ten_powers; mul_ext(e,&r_big_ten_powers[findex],e); *decpt += findex * TP; pp = &ten_powers[1]; while(pp < &ten_powers[TP] && cmp_ext(e, pp) >= 0) pp++; pp--; findex = pp - ten_powers; *decpt += findex; if (cmp_ext(e, &ten_powers[0]) < 0) { pp = &r_big_ten_powers[1]; while(cmp_ext(e,pp) < 0) pp++; pp--; findex = pp - r_big_ten_powers; mul_ext(e, &big_ten_powers[findex], e); *decpt -= findex * TP; /* here, value >= 10 ** -28 */ ten_mult(e); (*decpt)--; pp = &r_ten_powers[0]; while(cmp_ext(e, pp) < 0) pp++; findex = pp - r_ten_powers; mul_ext(e, &ten_powers[findex], e); *decpt -= findex; findex = 0; } (*decpt)++; /* because now value in [1.0, 10.0) */ } if (! ecvtflag) { /* for fcvt() we need ndigit digits behind the dot */ pe += *decpt; if (pe > &buf[NDIGITS]) pe = &buf[NDIGITS]; } m.exp = -62; m.sign = 0; m.m1 = 0xA0000000; m.m2 = 0; while (p <= pe) { struct EXTEND oneminm; if (p - pe > NSIGNIFICANT) { findex = 0; e->m1 = 0; } if (findex) { struct EXTEND tc, oldtc; int count = 0; oldtc.exp = 0; oldtc.sign = 0; oldtc.m1 = 0; oldtc.m2 = 0; tc = ten_powers[findex]; while (cmp_ext(e, &tc) >= 0) { oldtc = tc; add_ext(&tc, &ten_powers[findex], &tc); count++; } *p++ = count + '0'; oldtc.sign = 1; add_ext(e, &oldtc, e); findex--; continue; } if (e->m1) { m.sign = 1; add_ext(&ten_powers[0], &m, &oneminm); m.sign = 0; if (e->exp >= 0) { struct EXTEND x; x.m2 = 0; x.exp = e->exp; x.sign = 1; x.m1 = e->m1>>(31-e->exp); *p++ = (x.m1) + '0'; x.m1 = x.m1 << (31-e->exp); add_ext(e, &x, e); } else *p++ = '0'; /* Check that remainder is still significant */ if (cmp_ext(&m, e) > 0 || cmp_ext(e, &oneminm) > 0) { if (e->m1 && e->exp >= -1) *(p-1) += 1; e->m1 = 0; continue; } ten_mult(&m); ten_mult(e); } else *p++ = '0'; } if (pe >= buf) { p = pe; *p += 5; /* round of at the end */ while (*p > '9') { *p = '0'; if (p > buf) ++*--p; else { *p = '1'; ++*decpt; if (! ecvtflag) { /* maybe add another digit at the end, because the point was shifted right */ if (pe > buf) *pe = '0'; pe++; } } } *pe = '\0'; } return buf; } _dbl_ext_cvt(double value, struct EXTEND *e) { /* Convert double to extended */ int exponent; value = frexp(value, &exponent); e->sign = value < 0.0; if (e->sign) value = -value; e->exp = exponent - 1; value *= 4294967296.0; e->m1 = value; value -= e->m1; value *= 4294967296.0; e->m2 = value; } static struct EXTEND max_d; double _ext_dbl_cvt(struct EXTEND *e) { /* Convert extended to double */ double f; int sign = e->sign; e->sign = 0; if (e->m1 == 0 && e->m2 == 0) { return 0.0; } if (max_d.exp == 0) { _dbl_ext_cvt(DBL_MAX, &max_d); } if (cmp_ext(&max_d, e) < 0) { f = HUGE_VAL; errno = ERANGE; } else f = ldexp((double)e->m1*4294967296.0 + (double)e->m2, e->exp-63); if (sign) f = -f; if (f == 0.0 && (e->m1 != 0 || e->m2 != 0)) { errno = ERANGE; } return f; }