ecvt.c   [plain text]


/*
 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * Copyright (c) 1999-2003 Apple Computer, Inc.  All Rights Reserved.
 * 
 * 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@
 */
/*
 *	ecvt converts to decimal
 *	the number of digits is specified by ndigit
 *	decptp is set to the position of the decimal point
 *	signp is set to 0 for positive, 1 for negative
 */


static double ecvt_rint(double x);
static double ecvt_copysign(double x, double y);

static char *cvt();

/* big enough to handle %.20f conversion of 1e308 */
#define	NDIG		350

char*
ecvt(arg, ndigits, decptp, signp)
double arg;
int ndigits, *decptp, *signp;
{
	return(cvt(arg, ndigits, decptp, signp, 1));
}

char*
fcvt(arg, ndigits, decptp, signp)
double arg;
int ndigits, *decptp, *signp;
{
	return(cvt(arg, ndigits, decptp, signp, 0));
}

static char*
cvt(arg, ndigits, decptp, signp, eflag)
double arg;
int ndigits, *decptp, *signp;
int eflag;
{
	register int decpt;
	double fi, fj;
	register char *p, *p1;
	static char buf[NDIG] = { 0 };
	double modf();

	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);
			/**--p1 = (int)((fj+.03)*10) + '0';*/
			*--p1 = (int)ecvt_rint((fj)*10) + '0';
			decpt++;
		}
		while (p1 < &buf[NDIG])
			*p++ = *p1++;
	} else if (arg > 0) {
		while ((fj = arg*10) < 1) {
			arg = fj;
			decpt--;
		}
	}
	*decptp = decpt;

	/*
	 * do fraction 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 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 ecvt_rint(double x)
{
	asm("frndint" : "=t" (x) :  "0" (x));
	return(x);
}