From: bothner@cygnus.com
Subject: Re: does it bug of ostream.form()
Organization: GNUs Not Usenet
Distribution: gnu
Date: Thu, 27 Aug 1992 14:10:13 GMT

> 1:
> The output of this tiny program is a little bit strange.
> Only when form() output 20.0, the format changes.

This was a problem in libg++/iostream/outfloat.C.
I've appended my curent version.  I've no idea if how
compatible it is with libg++-2.2.

> 2:
> This is another bug. Because I don't want such a dirty format above, I
> changed the "cout << form()" to "printf()" then interesting accident ...
> I think everyone will expect the output below ...

No.  stdout and cout are different objects, with different buffers.
They are not synchonized (unless you use the stdio implementation
in libg++/iostream/stdio - in whcih case you will get the
formatting bug with printf).

The same will happen for most implementations of iostreams.

	--Per Bothner
Cygnus Support     bothner@cygnus.com

//    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"

// 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)
{
#define PUT(x) do {if (sb->sputc(x) < 0) goto error;} while (0)
#define PUTN(p, n) \
   do {int __n=n;if (sb->sputn(p, __n) != __n) goto error;} while (0)
#define PADN(fill, n) do {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 'F':
	exp = 'e';
	mode = 0;
	skip_zeroes = 1;
	type = 'g';
	break;
      case 'e':
	exp = 'e';
	mode = 2;
	precision++;  // Add one to include digit before decimal point.
	break;
      case 'E':
	exp = 'E';
	mode = 2;
	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 */
    char *p = dtoa(value, mode, precision, &decpt, &sign, &end);
    register int i;
    int useful_digits = end-p;
    char *exponent_start = EBUF_END;
    // 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 > 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 0;
  error:
    return EOF;
}

Newsgroups: gnu.g++.lib.bug