view src/libsys/sprintf/float.c @ 134:7d50d8d13711

FFS code sync with Magnetite + gcc version fix This change brings the new flash autodetection for FC and Pirelli targets from Magnetite, and should also fix the gcc version for C1xx and gtamodem targets, which were previously broken because they used TI's original flash autodetect code (which operates at address 0) while the boot ROM is mapped there.
author Mychaela Falconia <falcon@freecalypso.org>
date Tue, 11 Dec 2018 08:43:25 +0000
parents 425ab6d987f3
children
line wrap: on
line source

/*
 * Embedded [v]sprintf() implementation by Mychaela Falconia,
 * loosely based on the 4.3BSD-Tahoe version.
 *
 * This module contains the floating point conversion functions.
 */

#include <sys/types.h>
#include <ctype.h>
#include "defs.h"

extern double modf();

extern u_char * _sprintf_field(u_char *op, int width, int flags, int sign,
			u_char *body, int size, int dprec, int fpprec);

#define	MAX_INT_DIGITS	10	/* 2^32-1 in decimal */
#define	MAXFRACT	16	/* max sensible for 64-bit double */
#define	DEFPREC		6

static char *
emit_integer_portion(unsigned number, char *endp)
{
	char *t = endp;

	do {
		*--t = tochar(number % 10);
		number /= 10;
	} while (number);
	return (t);
}

static int
f_round(double fract, char *start, char *end, int sign)
{
	double tmp;

	(void)modf(fract * 10, &tmp);
	if (tmp > 4)
		for (;; --end) {
			if (*end == '.')
				--end;
			if (++*end <= '9')
				break;
			*end = '0';
			if (end == start) {
				*--end = '1';
				return(1);
			}
		}
	/* ``"%.3f", (double)-0.0004'' gives you a negative 0. */
	else if (sign == '-')
		for (;; --end) {
			if (*end == '.')
				--end;
			if (*end != '0')
				break;
			if (end == start)
				return(-1);	/* clear the -ve */
		}
	return(0);
}

u_char *
_sprintf_percent_f(u_char *op, int width, int flags, int sign,
		double number, int prec)
{
	char buf[MAX_INT_DIGITS + 1 + MAXFRACT];
	int extra_prec = 0;
	int origsign = sign;
	int round_stat;
	double fract, tmp;
	char *start, *t;

	/* first order of business: weed out infinities and NaNs */
	if (isinf(number)) {
		if (number < 0)
			sign = '-';
		return _sprintf_field(op, width, flags, sign, "Inf", 3, 0, 0);
	}
	if (isnan(number))
		return _sprintf_field(op, width, flags, sign, "NaN", 3, 0, 0);
	/* OK, we know it's a valid real like in the good old VAX days */
	if (number < 0) {
		sign = '-';
		number = -number;
	}
	fract = modf(number, &tmp);
	if (tmp > (double) 0xFFFFFFFF)
		return _sprintf_field(op, width, flags, sign, "Toobig", 6,
					0, 0);
	start = emit_integer_portion((unsigned) tmp, buf + MAX_INT_DIGITS);
	if (prec > MAXFRACT) {
		extra_prec = prec - MAXFRACT;
		prec = MAXFRACT;
	} else if (prec == -1)
		prec = DEFPREC;
	t = buf + MAX_INT_DIGITS;
	/*
	 * if precision required or alternate flag set, add in a
	 * decimal point.
	 */
	if (prec || flags&ALT)
		*t++ = '.';
	/* if requires more precision and some fraction left */
	if (fract) {
		if (prec)
			do {
				fract = modf(fract * 10, &tmp);
				*t++ = tochar((int)tmp);
			} while (--prec && fract);
		if (fract) {
			round_stat = f_round(fract, start, t - 1, sign);
			if (round_stat == 1)
				start--;
			else if (round_stat == -1)
				sign = origsign;
		}
	}
	return _sprintf_field(op, width, flags, sign, start, t - start,
				0, prec + extra_prec);
}