/* * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ /* Copyright (c) 1992 NeXT Computer, Inc. All rights reserved. * * File: libc/m98k/gen/ecvt.c * * char *ecvt(double x, int ndigits, int *decimal, int *sign); * char *fcvt(double x, int ndigits, int *decimal, int *sign); * * The function `ecvt' converts the double `x' to a null-terminated * string of `ndigits' ASCII digits and returns a pointer to the string. * The position of the decimal point relative to the beginning of the * string is stored in the int pointed to by `decimal'. A negative * value means that the decimal point appears to the left of the returned * digits. If the sign of the result is negative, a non-zero value is * stored in the int pointed to by `sign'; otherwise, a zero value is stored. * The low-order digit of the returned value is rounded. * * The function `fcvt' is identical to `ecvt', except that the correct digit * has been rounded for Fortran F-format output of the number of digits * specified by `ndigits'. * * HISTORY * 10-Nov-92 Derek B Clegg (dclegg@next.com) * Ported to m98k. * 8-Jan-92 Peter King (king@next.com) * Created from M68K sources which was created from VAX sources. */ #import <math.h> static double ecvt_rint(double x); static double ecvt_copysign(double x, double y); static char *cvt(double arg, int ndigits, int *decptp, int *signp, int eflag); #define isNAN(x) ((x) != (x)) /* big enough to handle %.20f conversion of 1e308 */ #define NDIG 350 char * ecvt(double arg, int ndigits, int *decptp, int *signp) { return (cvt(arg, ndigits, decptp, signp, 1)); } char * fcvt(double arg, int ndigits, int *decptp, int *signp) { return (cvt(arg, ndigits, decptp, signp, 0)); } static char * cvt(double arg, int ndigits, int *decptp, int *signp, int eflag) { int decpt; double fi, fj; char *p, *p1; static char buf[NDIG] = { 0 }; if (ndigits < 0) ndigits = 0; if (ndigits >= NDIG - 1) ndigits = NDIG - 2; decpt = 0; *signp = 0; p = &buf[0]; if (arg == 0) { *decptp = 0; while (p < &buf[ndigits]) *p++ = '0'; *p = '\0'; return (buf); } else if (arg < 0) { *signp = 1; arg = -arg; } arg = modf(arg, &fi); p1 = &buf[NDIG]; /* Do integer part */ if (fi != 0) { while (fi != 0) { fj = modf(fi/10, &fi); #if 0 *--p1 = (int)((fj + 0.03) * 10) + '0'; #else *--p1 = (int)ecvt_rint(fj * 10) + '0'; #endif decpt++; } while (p1 < &buf[NDIG]) *p++ = *p1++; } else if (arg > 0) { while ((fj = arg*10) < 1) { arg = fj; decpt--; } } *decptp = decpt; /* Do the fractional part. * p pts to where fraction should be concatenated. * p1 is how far conversion must go to. */ p1 = &buf[ndigits]; if (eflag == 0) { /* fcvt must provide ndigits after decimal pt */ p1 += decpt; /* if decpt was negative, we might be done for fcvt */ if (p1 < &buf[0]) { buf[0] = '\0'; return (buf); } } while (p <= p1 && p < &buf[NDIG]) { arg *= 10; arg = modf(arg, &fj); *p++ = (int)fj + '0'; } /* If we converted all the way to the end of the buf, don't mess with * rounding since there's nothing significant out here anyway. */ if (p1 >= &buf[NDIG]) { buf[NDIG-1] = '\0'; return (buf); } /* Round by adding 5 to last digit and propagating carries. */ p = p1; *p1 += 5; while (*p1 > '9') { *p1 = '0'; if (p1 > buf) { ++*--p1; } else { *p1 = '1'; (*decptp)++; if (eflag == 0) { if (p > buf) *p = '0'; p++; } } } *p = '\0'; return (buf); } static double L = 4503599627370496.0E0; /* 2**52 */ static int ecvt_init = 0; /* * FIXME: This deserves a comment if you turn this off! * This used to #pragma CC_OPT_OFF. * (Probably this was because the isNAN test was optimized away.) * Why don't we just use the value of L given above? */ static double ecvt_rint(double x) { double s, t, one; one = 1.0; if (ecvt_init == 0) { int i; L = 1.0; for (i = 52; i != 0; i--) L *= 2.0; ecvt_init = 1; } if (isNAN(x)) return (x); if (ecvt_copysign(x, one) >= L) /* already an integer */ return (x); s = ecvt_copysign(L, x); t = x + s; /* x+s rounded to integer */ return (t - s); } /* Couldn't we use something like the following structure instead of the hacky unsigned short pointer stuff? struct double_format { unsigned sign:1; unsigned exponent:11; unsigned hi_fraction:20; unsigned lo_fraction:32; }; */ #define msign ((unsigned short)0x7fff) #define mexp ((unsigned short)0x7ff0) static double ecvt_copysign(double x, double y) { unsigned short *px, *py; px = (unsigned short *)&x; py = (unsigned short *)&y; *px = (*px & msign) | (*py & ~msign); return (x); } /* * This used to #pragma CC_OPT_ON */