/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 1988 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/* Conversion between binary and decimal floating point. */
#include "base_conversion.h"
/* PRIVATE FUNCTIONS */
/*
* Rounds decimal record *pd according to modes in *pm, recording exceptions
* for inexact or overflow in *ps. round is the round digit and sticky is 0
* or non-zero to indicate exact or inexact. pd->ndigits is expected to be
* correctly set.
*/
void
decimal_round(decimal_mode *pm, decimal_record *pd, fp_exception_field_type *ps,
char round, unsigned sticky)
{
int lsd, i;
if ((round == '0') && (sticky == 0)) { /* Exact. */
goto done;
}
*ps |= 1 << fp_inexact;
switch (pm->rd) {
case fp_nearest:
if (round < '5')
goto done;
if (round > '5')
goto roundup;
if (sticky != 0)
goto roundup;
/* Now in ambiguous case; round up if lsd is odd. */
if (pd->ndigits <= 0)
goto done; /* Presumed 0. */
lsd = pd->ds[pd->ndigits - 1] - '0';
if ((lsd % 2) == 0)
goto done;
goto roundup;
case fp_positive:
if (pd->sign != 0)
goto done;
goto roundup;
case fp_negative:
if (pd->sign == 0)
goto done;
goto roundup;
case fp_tozero:
goto done;
}
roundup:
for (i = (pd->ndigits - 1); (pd->ds[i] == '9') && (i >= 0); i--)
pd->ds[i] = '0';
if (i >= 0)
(pd->ds[i])++;
else { /* Rounding carry out has occurred. */
pd->ds[0] = '1';
if (pm->df == floating_form) { /* For E format, simply
* adjust exponent. */
pd->exponent++;
} else { /* For F format, increase length of string. */
if (pd->ndigits > 0)
pd->ds[pd->ndigits] = '0';
pd->ndigits++;
}
}
goto ret;
done:
if (pd->ndigits <= 0) { /* Create zero string. */
pd->ds[0] = '0';
pd->ndigits = 1;
}
ret:
pd->ds[pd->ndigits] = 0;/* Terminate string. */
return;
}
/*
* Converts an unpacked integer value *pu into a decimal string in *ds, of
* length returned in *ndigs. Inexactness is indicated by setting
* ds[ndigs-1] odd.
*
* Arguments
* pu: Input unpacked integer value input.
* nsig: Input number of significant digits required.
* ds: Output decimal integer string output
* must be large enough.
* nzeros: Output number of implicit trailing zeros
* produced.
* ndigs: Output number of explicit digits produced
* in ds.
*/
void
binary_to_decimal_integer(unpacked *pu, unsigned nsig, char ds[],
unsigned *nzeros, unsigned *ndigs)
{
_big_float *pd, b, d;
int e, i, is;
_BIG_FLOAT_DIGIT stickyshift;
char s[4];
b.bsize = _BIG_FLOAT_SIZE; /* Initialize sizes of big floats. */
d.bsize = _BIG_FLOAT_SIZE;
_unpacked_to_big_float(pu, &b, &e);
if (e < 0) {
_right_shift_base_two(&b, (short unsigned) -e, &stickyshift);
#ifdef DEBUG
assert(stickyshift == 0);
#endif
}
_big_binary_to_big_decimal(&b, &d);
if (e <= 0)
pd = &d;
else {
_big_float_times_power(&d, 2, e, (int) nsig, &pd);
switch ((unsigned int)pd) {
case ((unsigned int)BIG_FLOAT_TIMES_TOOBIG):
{
char bcastring[80];
(void) sprintf(bcastring, " binary exponent %d ", e);
_base_conversion_abort(ERANGE, bcastring);
break;
}
case ((unsigned int)BIG_FLOAT_TIMES_NOMEM):
{
char bcastring[80];
(void) sprintf(bcastring, " binary exponent %d ", e);
_base_conversion_abort(ENOMEM, bcastring);
break;
}
default:
#ifdef DEBUG
if (pd != &d)
(void) printf(" large binary exponent %d needs heap buffer \n", e);
printf(" product ");
_display_big_float(pd, 10);
#endif
break;
}
}
_fourdigitsquick((short unsigned) pd->bsignificand[pd->blength - 1], s);
for (i = 0; s[i] == '0'; i++); /* Find first non-zero digit. */
for (is = 0; i <= 3;)
ds[is++] = s[i++]; /* Copy subsequent digits. */
for (i = (pd->blength - 2); i >= 0; i--) { /* Convert powers of
* 10**4 to decimal
* digits. */
_fourdigitsquick((short unsigned) pd->bsignificand[i], &(ds[is]));
is += 4;
}
ds[is] = 0;
*ndigs = is;
*nzeros = pd->bexponent;
if (pd != &d)
_free_big_float(pd);
#ifdef DEBUG
printf(" binary to decimal integer result %s * 10**%d \n", ds, *nzeros);
#endif
}
/*
* Converts an unpacked fraction value *pu into a decimal string consisting
* of a) an implicit '.' b) *nzeros implicit leading zeros c) *ndigs explicit
* digits in ds ds contains at least nsig significant digits. nzeros + *
* *ndigs is at least nfrac digits after the point. Inexactness is indicated
* by sticking to the lsb.
*
* Arguments
*
* pu: Input unpacked fraction value output < 1
* in magnitude.
* nsig: Input number of significant digits
* required.
* nfrac: Input number of digits after point
* required.
* ds: Output decimal integer string output -
* must be large enough.
* nzeros: Output number of implicit leading zeros
* produced.
* ndigs: Output number of explicit digits produced
* in ds.
*/
void
binary_to_decimal_fraction(unpacked *pu, unsigned nsig, unsigned nfrac,
char ds[], int *nzeros, int *ndigs)
{
_big_float *pb, b, d;
int e, i, j, is, excess;
char s[4];
int tensig, tenpower;
_BIG_FLOAT_DIGIT stickyshift;
*nzeros = 0;
if (pu->fpclass == fp_zero) { /* Exact zero. */
for (i = 0; i <= nfrac; i++)
ds[i] = '0';
for (; i <= nsig; i++)
ds[i] = '0';
*ndigs = i;
return;
}
b.bsize = _BIG_FLOAT_SIZE; /* Initialize sizes of big floats. */
d.bsize = _BIG_FLOAT_SIZE;
_unpacked_to_big_float(pu, &b, &e);
/*
* e < 0 always
*/
b.bexponent = e;
tenpower = nsig + (int) (((17 - e - 16 * b.blength) * (unsigned long) 19729) >> 16);
if (tenpower < nfrac)
tenpower = nfrac;
tensig = nfrac;
if (nsig > tensig)
tensig = nsig;
tensig = 1 + (((tensig + 2) * 217706) >> 16);
tensig = -tensig;
#ifdef DEBUG
printf(" binary to decimal fraction exponent 2**%d \n", e);
printf(" binary to decimal fraction nsig %d nfrac %d tenpower %d tensig %d \n", nsig, nfrac, tenpower, tensig);
#endif
_big_float_times_power(&b, 10, tenpower, tensig, &pb);
switch ((unsigned int)pb) {
case ((unsigned int)BIG_FLOAT_TIMES_TOOBIG):
{
char bcastring[80];
(void) sprintf(bcastring, " decimal exponent %d ", tenpower);
_base_conversion_abort(ERANGE, bcastring);
break;
}
case ((unsigned int)BIG_FLOAT_TIMES_NOMEM):
{
char bcastring[80];
(void) sprintf(bcastring, " decimal exponent %d ", tenpower);
_base_conversion_abort(ENOMEM, bcastring);
break;
}
default:
#ifdef DEBUG
if (pb != &b)
printf(" large decimal exponent %d needs heap buffer \n", tenpower);
printf(" product ");
_display_big_float(pb, 2);
#endif
break;
}
if (pb->bexponent <= -16) {
/* Have computed appropriate decimal part; now toss fraction. */
excess = (-pb->bexponent) / 16;
#ifdef DEBUG
printf(" discard %d excess fraction bits \n", 16 * excess);
#endif
for (i = 0; (i < excess) && (pb->bsignificand[i] == 0); i++);
if (i < excess)
pb->bsignificand[excess] |= 1; /* Sticky bit for
* discarded fraction. */
for (i = excess; i < pb->blength; i++)
pb->bsignificand[i - excess] = pb->bsignificand[i];
pb->blength -= excess;
pb->bexponent += 16 * excess;
}
if (pb->bexponent < 0) {
_right_shift_base_two(pb, (short unsigned) -pb->bexponent, &stickyshift);
if (stickyshift != 0)
pb->bsignificand[0] |= 1; /* Stick to lsb. */
}
_big_binary_to_big_decimal(pb, &d);
i = d.blength - 1;
while (d.bsignificand[i] == 0)
i--;
_fourdigitsquick((short unsigned) d.bsignificand[i], s);
for (j = 0; s[j] == '0'; j++); /* Find first non-zero digit. */
for (is = 0; j <= 3;)
ds[is++] = s[j++]; /* Copy subsequent digits. */
for (i--; i >= 0; i--) {/* Convert powers of 10**4 to decimal digits. */
_fourdigitsquick((short unsigned) d.bsignificand[i], &(ds[is]));
is += 4;
}
ds[is] = 0;
*ndigs = is;
#ifdef DEBUG
assert(tenpower >= is);
#endif
*nzeros = tenpower - is;/* There were supposed to be tenpower leading
* digits, and is were found. */
if (pb != &b)
_free_big_float(pb);
#ifdef DEBUG
printf(" binary to decimal fraction result .%s * 10**%d \n", ds, -(*nzeros));
#endif
}
void
_unpacked_to_decimal(unpacked *px, decimal_mode *pm, decimal_record *pd,
fp_exception_field_type *ps)
{
unpacked fx, ix;
unsigned fmask, imask;
int i, intdigs, fracdigs, fraczeros, fracsigs, ids, idsbound, lzbound;
unsigned nsig, nfrac, intzeros, intsigs;
char is[_INTEGER_SIZE], fs[DECIMAL_STRING_LENGTH];
char round = '0';
unsigned sticky = 0;
pd->sign = px->sign;
pd->fpclass = px->fpclass;
if ((px->fpclass != fp_normal) && (px->fpclass != fp_subnormal))
return;
if ((pm->ndigits >= DECIMAL_STRING_LENGTH) ||
((pm->df == floating_form) && (pm->ndigits < 1))) { /* Gross overflow or bad
* spec. */
overflow:
*ps |= 1 << fp_overflow;
return;
}
/* Divide x up into integer part ix and fraction part fx. */
ix = *px;
fx = ix;
if (ix.exponent <= -1) {/* All fraction. */
ix.fpclass = fp_zero;
} else if (ix.exponent >= 159) { /* All integer. */
fx.fpclass = fp_zero;
} else if ((ix.exponent % 32) == 31) { /* Integer/fraction boundary
* is conveniently on a word
* boundary. */
imask = (ix.exponent + 1) / 32; /* Words 0..imask-1 are
* integer; imask..SIZE are
* fraction. */
for (i = 0; i < imask; i++)
fx.significand[i] = 0;
for (; i < UNPACKED_SIZE; i++)
ix.significand[i] = 0;
_fp_normalize(&fx);
} else { /* Integer/fraction boundary falls in the
* middle of a word. */
imask = (ix.exponent + 1) / 32; /* Words 0..imask-1 are
* integer; imask is integer
* and fraction ;
* imask+1..SIZE are
* fraction. */
for (i = 0; i < imask; i++)
fx.significand[i] = 0;
fmask = (1 << (31 - (ix.exponent % 32))) - 1;
fx.significand[imask] &= fmask;
ix.significand[imask] &= ~fmask;
for (i = (imask + 1); i < UNPACKED_SIZE; i++)
ix.significand[i] = 0;
_fp_normalize(&fx);
}
if (ix.fpclass != fp_zero) { /* Compute integer part of result. */
if (pm->df == floating_form)
nsig = pm->ndigits + 1; /* Significant digits wanted
* for E format, plus one for
* rounding. */
else
nsig = _INTEGER_SIZE; /* Significant digits wanted
* for F format == all. */
binary_to_decimal_integer(&ix, nsig, is, &intzeros, &intsigs);
} else {
intsigs = 0;
intzeros = 0;
}
intdigs = intsigs + intzeros;
fracdigs = 0;
if (((pm->df == fixed_form) && (pm->ndigits >= 0)) ||
((pm->df == floating_form) && ((pm->ndigits + 1) > intdigs))) { /* Need to compute
* fraction part. */
if (pm->df == floating_form) { /* Need more significant
* digits. */
nsig = pm->ndigits + 2 - intdigs; /* Add two for rounding,
* sticky. */
if (nsig > DECIMAL_STRING_LENGTH)
nsig = DECIMAL_STRING_LENGTH;
nfrac = 1;
} else { /* Need fraction digits. */
nsig = 0;
nfrac = pm->ndigits + 2; /* Add two for rounding,
* sticky. */
if (nfrac > DECIMAL_STRING_LENGTH)
nfrac = DECIMAL_STRING_LENGTH;
}
binary_to_decimal_fraction(&fx, nsig, nfrac, fs, &fraczeros, &fracsigs);
fracdigs = fraczeros + fracsigs;
}
if (pm->df == floating_form) { /* Combine integer and fraction for E
* format. */
idsbound = intsigs;
if (idsbound > pm->ndigits)
idsbound = pm->ndigits;
for (ids = 0; ids < idsbound; ids++)
pd->ds[ids] = is[ids];
/* Put integer into output string. */
idsbound = intsigs + intzeros;
if (idsbound > pm->ndigits)
idsbound = pm->ndigits;
for (; ids < idsbound; ids++)
pd->ds[ids] = '0';
if (ids == pm->ndigits) { /* Integer part had enough
* significant digits. */
pd->ndigits = ids;
pd->exponent = intdigs - ids;
if (ids < intdigs) { /* Gather rounding info. */
if (ids < intsigs)
round = is[ids++];
else
round = '0';
for (; (is[ids] == '0') && (ids < intsigs); ids++);
if (ids < intsigs)
sticky = 1;
if (fx.fpclass != fp_zero)
sticky = 1;
} else {/* Integer part is exact - round from
* fraction. */
if (fx.fpclass != fp_zero) {
int stickystart;
/* Fraction non-zero. */
if (fraczeros > 0) { /* Round digit is zero. */
round = '0';
stickystart = 0; /* Stickies start with
* fs[0]. */
} else { /* Round digit is fs[0]. */
round = fs[0];
stickystart = 1; /* Stickies start with
* fs[1]. */
}
if (sticky == 0) { /* Search for sticky
* bits. */
for (ids = stickystart; (fs[ids] == '0') && (ids < fracdigs); ids++);
if (ids < fracdigs)
sticky = 1;
}
}
}
} else { /* Need more significant digits from fraction
* part. */
idsbound = pm->ndigits - ids;
if (ids == 0) { /* No integer part - find first
* significant digit. */
for (i = 0; fs[i] == '0'; i++);
idsbound = i + idsbound + fraczeros;
i += fraczeros; /* Point i at first
* significant digit. */
} else
i = 0;
if (idsbound > fracdigs)
idsbound = fracdigs;
pd->exponent = -idsbound;
if (fraczeros < idsbound) /* Compute number of
* leading zeros
* required. */
lzbound = fraczeros;
else
lzbound = idsbound;
for (; (i < lzbound); i++)
pd->ds[ids++] = '0';
for (; (i < idsbound); i++)
pd->ds[ids++] = fs[i - fraczeros];
i -= fraczeros; /* Don't worry about leading zeros
* from now on, we're just rounding */
if (i < fracsigs) { /* Gather rounding info. */
if (i < 0)
round = '0';
else
round = fs[i];
i++;
if (sticky == 0) { /* Find out if remainder
* is exact. */
if (i < 0)
i = 0;
for (; (fs[i] == '0') && (i < fracsigs); i++);
if (i < fracsigs)
sticky = 1;
}
} else {/* Fraction part is exact - add zero digits
* if required. */
for (; ids < pm->ndigits; ids++)
pd->ds[ids] = '0';
}
pd->ndigits = ids;
}
decimal_round(pm, pd, ps, round, sticky);
} else { /* Combine integer and fraction for F format. */
if (pm->ndigits >= 0) { /* Normal F format. */
if ((intdigs + pm->ndigits) >= DECIMAL_STRING_LENGTH)
goto overflow;
for (ids = 0; ids < intsigs; ids++)
pd->ds[ids] = is[ids];
for (; ids < intdigs; ids++)
pd->ds[ids] = '0';
/* Copy integer digits. */
idsbound = fracdigs;
if (idsbound > pm->ndigits)
idsbound = pm->ndigits;
if (fraczeros < idsbound) /* Compute number of
* leading zeros
* required. */
lzbound = fraczeros;
else
lzbound = idsbound;
for (i = 0; (i < lzbound); i++)
pd->ds[ids++] = '0';
for (; (i < idsbound); i++)
pd->ds[ids++] = fs[i - fraczeros]; /* Copy fraction digits. */
for (; i < pm->ndigits; i++)
pd->ds[ids++] = '0';
/* Copy trailing zeros if necessary. */
pd->ndigits = ids;
pd->exponent = intdigs - ids;
i -= fraczeros; /* Don't worry about leading zeros
* from now on, we're just rounding */
if (i < fracsigs) { /* Gather rounding info. */
if (i < 0)
round = '0';
else
round = fs[i];
i++;
if (sticky == 0) { /* Find out if remainder
* is exact. */
if (i < 0)
i = 0;
for (; (fs[i] == '0') && (i < fracsigs); i++);
if (i < fracsigs)
sticky = 1;
}
}
decimal_round(pm, pd, ps, round, sticky);
} else { /* Bizarre F format - round to left of point. */
int roundpos = -pm->ndigits;
if (intdigs >= DECIMAL_STRING_LENGTH)
goto overflow;
if (roundpos >= DECIMAL_STRING_LENGTH)
goto overflow;
if (intdigs <= roundpos) { /* Not enough integer
* digits. */
if (intdigs == roundpos) {
round = is[0];
i = 1;
} else {
round = '0';
i = 0;
}
for (; (is[i] == '0') && (i < intsigs); i++);
/* Search for sticky bits. */
if (i < intsigs)
sticky = 1;
pd->ndigits = 0;
} else {/* Some integer digits do not get rounded
* away. */
#ifdef _NO_GOOD
for (ids = 0; ids < (intsigs - roundpos); ids++)
pd->ds[ids] = is[ids];
for (ids = 0; ids < (intdigs - roundpos); ids++)
pd->ds[ids] = '0';
#else
{
int ncopy = intsigs - roundpos;
if (ncopy > 0) {
/* Copy integer digits. */
(void) memcpy(&(pd->ds[0]), &(is[0]), ncopy);
ids = ncopy;
}
}
{
int ncopy = intdigs - roundpos - ids ;
if (ncopy > 0) {
(void) memset(&(pd->ds[ids]), '0', ncopy);
ids += ncopy;
}
}
#endif /* _NO_GOOD */
/* Copy integer digits. */
pd->ndigits = ids;
if (ids < intsigs) { /* Inexact. */
round = is[ids++];
for (; (is[ids] == '0') && (ids < intsigs); ids++);
/* Search for non-zero digits. */
if (ids < intsigs)
sticky = 1;
}
}
if (fx.fpclass != fp_zero)
sticky = 1;
decimal_round(pm, pd, ps, round, sticky);
for (i = pd->ndigits; i < (pd->ndigits + roundpos); i++)
pd->ds[i] = '0'; /* Blank out rounded
* away digits. */
pd->exponent = 0;
pd->ndigits = i;
pd->ds[i] = 0; /* Terminate string. */
}
}
}
void
double_to_decimal(double *px, decimal_mode *pm, decimal_record *pd,
fp_exception_field_type *ps)
{
double_equivalence kluge;
unpacked u;
*ps = 0; /* Initialize *ps. */
kluge.x = *px;
pd->sign = kluge.f.msw.sign;
pd->fpclass = _class_double(px);
switch (pd->fpclass) {
case fp_zero:
break;
case fp_infinity:
break;
case fp_quiet:
break;
case fp_signaling:
break;
default:
_unpack_double(&u, &kluge.x);
_unpacked_to_decimal(&u, pm, pd, ps);
}
}
void
quadruple_to_decimal(quadruple *px, decimal_mode *pm, decimal_record *pd,
fp_exception_field_type *ps)
{
quadruple_equivalence kluge;
unpacked u;
int i;
*ps = 0; /* Initialize *ps - no exceptions. */
for (i = 0; i < 4; i++)
#ifdef __STDC__
kluge.x = *px;
#else
kluge.x.u[i] = px->u[i];
#endif
pd->sign = kluge.f.msw.sign;
pd->fpclass = _class_quadruple(px);
switch (pd->fpclass) {
case fp_zero:
break;
case fp_infinity:
break;
case fp_quiet:
break;
case fp_signaling:
break;
default:
_unpack_quadruple(&u, px);
_unpacked_to_decimal(&u, pm, pd, ps);
}
}