NetBSD/gnu/lib/libg++/iostream/outfloat.C

184 lines
5.6 KiB
C

// This is part of the iostream library, providing input/output for C++.
// Copyright (C) 1992 Per Bothner.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free
// Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#include "ioprivate.h"
// Format floating-point number and print them.
// Return number of chars printed, or EOF on error.
// sign_mode == '+' : print "-" or "+"
// sign_mode == ' ' : print "-" or " "
// sign_mode == '\0' : print "-' or ""
int __outfloat(double value, streambuf *sb, char type,
int width, int precision, ios::fmtflags flags,
char sign_mode, char fill)
{
int count = 0;
#define PUT(x) do {if (sb->sputc(x) < 0) goto error; count++;} while (0)
#define PUTN(p, n) \
do {int _n=n; count+=_n; if (sb->sputn(p,_n) != _n) goto error;} while(0)
#define PADN(fill, n) \
do {int _n = n; count+=_n; if (sb->padn(fill, _n) < 0) goto error;} while (0)
ios::fmtflags pad_kind = flags & (ios::left|ios::right|ios::internal);
int skip_zeroes = 0;
int show_dot = (flags & ios::showpoint) != 0;
int decpt;
int sign;
int mode;
#define EBUF_SIZE 12
#define EBUF_END &ebuf[EBUF_SIZE]
char ebuf[EBUF_SIZE];
char *end;
int exp = 0;
switch (type) {
case 'f':
mode = 3;
break;
case 'e':
case 'E':
exp = type;
mode = 2;
if (precision != 999)
precision++; // Add one to include digit before decimal point.
break;
case 'g':
case 'G':
exp = type == 'g' ? 'e' : 'E';
if (precision == 0) precision = 1;
if (!(flags & ios::showpoint))
skip_zeroes = 1;
type = 'g';
mode = 2;
break;
}
/* Do the actual convension */
if (precision == 999 && mode != 3)
mode = 0;
char *p = dtoa(value, mode, precision, &decpt, &sign, &end);
register int i;
int useful_digits = end-p;
char *exponent_start = EBUF_END;
if (mode == 0)
precision = useful_digits;
// Check if we need to emit an exponent.
if (mode != 3 && decpt != 9999) {
i = decpt - 1;
if ((type != 'g' && type != 'F') || i < -4 || i >= precision) {
// Print the exponent into ebuf.
// We write ebuf in reverse order (right-to-left).
char sign;
if (i >= 0)
sign = '+';
else
sign = '-', i = -i;
/* Note: ANSI requires at least 2 exponent digits. */
do {
*--exponent_start = (i % 10) + '0';
i /= 10;
} while (i >= 10);
*--exponent_start = i + '0';
*--exponent_start = sign;
*--exponent_start = exp;
}
}
int exponent_size = EBUF_END - exponent_start;
if (mode == 1)
precision = 1;
/* If we print an exponent, always show just one digit before point. */
if (exponent_size)
decpt = 1;
if (decpt == 9999) { // Infinity or NaN
decpt = useful_digits;
precision = 0;
show_dot = 0;
}
// dtoa truncates trailing zeroes. Set the variable trailing_zeroes to
// the number of 0's we have to add (after the decimal point).
int trailing_zeroes = 0;
if (skip_zeroes)
trailing_zeroes = 0;
else if (type == 'f')
trailing_zeroes = useful_digits <= decpt ? precision
: precision-(useful_digits-decpt);
else if (exponent_size) // 'e' 'E' or 'g' format using exponential notation.
trailing_zeroes = precision - useful_digits;
else // 'g' format not using exponential notation.
trailing_zeroes = useful_digits <= decpt ? precision - decpt
: precision-useful_digits;
if (trailing_zeroes < 0) trailing_zeroes = 0;
if (trailing_zeroes != 0 || useful_digits > decpt)
show_dot = 1;
int print_sign;
if (sign_mode == 0)
print_sign = sign ? '-' : 0;
else if (sign_mode == '+')
print_sign = sign ? '-' : '+';
else /* if (sign_mode == ' ') */
print_sign = sign ? '-' : ' ';
// Calculate the width (before padding).
int unpadded_width =
(print_sign != 0) + trailing_zeroes + exponent_size + show_dot
+ useful_digits
+ (decpt > useful_digits ? decpt - useful_digits
: decpt > 0 ? 0 : 1 - decpt);
int padding = width > unpadded_width ? width - unpadded_width : 0;
if (padding > 0
&& pad_kind != (ios::fmtflags)ios::left
&& pad_kind != (ios::fmtflags)ios::internal) // Default (right) adjust.
PADN(fill, padding);
if (print_sign)
PUT(print_sign);
if (pad_kind == (ios::fmtflags)ios::internal && padding > 0)
PADN(fill, padding);
if (decpt > 0) {
if (useful_digits >= decpt)
PUTN(p, decpt);
else {
PUTN(p, useful_digits);
PADN('0', decpt-useful_digits);
}
if (show_dot) {
PUT('.');
// Print digits after the decimal point.
if (useful_digits > decpt)
PUTN(p + decpt, useful_digits-decpt);
}
}
else {
PUT('0');
if (show_dot) {
PUT('.');
PADN('0', -decpt);
// Print digits after the decimal point.
PUTN(p, useful_digits);
}
}
PADN('0', trailing_zeroes);
if (exponent_size)
PUTN(exponent_start, exponent_size);
if (pad_kind == (ios::fmtflags)ios::left && padding > 0) // Left adjustment
PADN(fill, padding);
return count;
error:
return EOF;
}