This is the mail archive of the libstdc++@gcc.gnu.org mailing list for the libstdc++ project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[v3] FP printing, take 2


Here's a revision.  I fixed the long lines and replaced log10 in
locale_facets.tcc with __builtin_log10 to avoid including cmath there.

I also uglified everything that shows up as a symbol in floatconv.o.

At this point I see two questions remaining.  What should the linker map
section actually be called, and does floatconv.cc need to be uglified further.

I think further discussion of GMP can wait until this is in.

Final comments?  Approvals :-)?



2004-08-29  Jerry Quinn  jlquinn@optonline.net

	* config/linker-map.gnu (GLIBCXX_3.4.2): Add dec_exponent,
        float_to_char_sprintf, dtoa.
	* include/bits/locale_facets.tcc (__float_to_char_fixed,
	__float_to_char_scientific, __float_to_char): New.
	(__dec_exponent, __dtoa, __float_to_char_sprintf): Declare.
	(num_put::_M_insert_float): Use __float_to_char instead of
        __convert_from_v.
	* Makefile.am (sources):  Add floatconv.cc.
	* Makefile.in: Regenerate.
	* src/floatconv.cc.


Index: config/linker-map.gnu
===================================================================
RCS file: /cvs/gcc/gcc/libstdc++-v3/config/linker-map.gnu,v
retrieving revision 1.68
diff -u -r1.68 linker-map.gnu
--- config/linker-map.gnu	30 Aug 2004 18:22:00 -0000	1.68
+++ config/linker-map.gnu	31 Aug 2004 00:43:48 -0000
@@ -259,6 +259,10 @@
     _ZN9__gnu_cxx17__pool_alloc_base16_M_get_free_listE[jm];
     _ZN9__gnu_cxx17__pool_alloc_base12_M_get_mutexEv;
 
+    _ZSt14__dec_exponentI[de]EiT_RiRb;
+    _ZSt23__float_to_char_sprintfI[de]EiPcT_iiSt13_Ios_Fmtflags;
+    _ZSt6__dtoaI[de]EPcS0_T_iiibPi;
+
 } GLIBCXX_3.4.1;
 
 # Symbols in the support library (libsupc++) have their own tag.
Index: include/bits/locale_facets.tcc
===================================================================
RCS file: /cvs/gcc/gcc/libstdc++-v3/include/bits/locale_facets.tcc,v
retrieving revision 1.203
diff -u -r1.203 locale_facets.tcc
--- include/bits/locale_facets.tcc	29 Aug 2004 09:04:33 -0000	1.203
+++ include/bits/locale_facets.tcc	31 Aug 2004 00:43:49 -0000
@@ -1037,16 +1037,254 @@
       __len = __newlen;
     }
 
-  // The following code uses snprintf (or sprintf(), when
-  // _GLIBCXX_USE_C99 is not defined) to convert floating point values
-  // for insertion into a stream.  An optimization would be to replace
-  // them with code that works directly on a wide buffer and then use
-  // __pad to do the padding.  It would be good to replace them anyway
-  // to gain back the efficiency that C++ provides by knowing up front
-  // the type of the values to insert.  Also, sprintf is dangerous
-  // since may lead to accidental buffer overruns.  This
-  // implementation follows the C++ standard fairly directly as
-  // outlined in 22.2.2.2 [lib.locale.num.put]
+  // Prints fixed point number to sbuf
+  //
+  // atoms contains numeric and exponent wide chars to use for the result.
+  // dpchar is the decimal point char to use.
+  //
+  // The number consists of buf and decp.  buf contains the sigificant digits
+  // of the number with no trailing 0's.  decp is the position of the decimal
+  // point.  It is the number of digits in buf to the left of the decimal
+  // point.
+  //
+  // prec specifies the maximum number of digits to print after the decimal
+  // point.  flags is provided for showpoint.  showpoint prints a point even
+  // if there are no more digits to print after the point.
+  //
+  // pad is true if exactly prec digits are to be printed after the point.  If
+  // there are not enough, 0's are added to the end.
+  template<typename _CharT>
+    int
+    __float_to_char_fixed(_CharT* __sbuf, const _CharT* __atoms,
+			  _CharT __dpchar, const char* __buf, int __decp, 
+			  int __prec, ios_base::fmtflags __flags, bool __pad)
+    {
+      _CharT* __sb = __sbuf;
+      const _CharT* __digits = __atoms + __num_base::_S_odigits;
+      bool __showpoint = __flags & ios_base::showpoint;
+
+      // Print integer portion
+      if (__decp > 0)
+	{
+	  while (*__buf && __decp) {
+	    *__sbuf++ = __digits[*__buf++ - '0'];
+	    __decp--;
+	  }
+	  // Fill with 0's if we ran out of digits.  If there were enough,
+	  // decp will be 0.
+	  for (; __decp; __decp--)
+	    *__sbuf++ = __digits[0];
+	}
+      else
+	// Leading 0 for fractional only numbers and zero.
+	*__sbuf++ = __digits[0];
+
+      // Print decimal point
+      // Showpoint always prints point.  Otherwise when prec > 0, there must
+      // be digits to print or forced padding.
+      if ((__prec && (__pad || *__buf))	|| __showpoint)
+	*__sbuf++ = __dpchar;
+
+
+      // Print 0's between decimal point and first significant digit
+      if (__decp < 0) {
+	while (__decp < 0 && __prec > 0) {
+	  *__sbuf++ = __digits[0];
+	  __decp++;
+	  __prec--;
+	}
+      }
+
+      // Print significant fractional digits
+      // At this point decp <= 0
+      for (; __prec > 0 && *__buf; __prec--)
+	*__sbuf++ = __digits[*__buf++ - '0'];
+      if (__pad) {
+	while (__prec-- > 0)
+	  *__sbuf++ = __digits[0];
+      }
+
+      return __sbuf - __sb;
+    }
+
+  // Prints scientific format number to sbuf
+  //
+  // Prints a number in scientific format i.e. +-d.ddddE+ee.
+  //
+  // atoms contains numeric and exponent wide chars to use for the result.
+  // dpchar is the decimal point char to use.
+  //
+  // The number consists of buf and decp.
+  // buf contains the sigificant digits of the number with no trailing 0's.
+  // decp is the position of the decimal point.  It is the number of digits
+  // to the left of the decimal point.
+  //
+  // prec specifies the maximum number of digits to print after the decimal
+  // point in scientific form.  showpoint prints a point even if there are no
+  // more digits to print after the point.
+  //
+  // pad is true if exactly prec digits are to be printed after the point.  If
+  // there are not enough, 0's are added to the end.
+  template<typename _CharT>
+    int
+    __float_to_char_scientific(_CharT* __sbuf, const _CharT* __atoms,
+			       _CharT __dpchar, const char* __buf, int __decp,
+			       int __prec, ios_base::fmtflags __flags, 
+			       bool __pad)
+    {
+      const _CharT* __digits = __atoms + __num_base::_S_odigits;
+      _CharT* __sb = __sbuf;
+      const char* __bp = __buf;
+      bool __showpoint = __flags & ios_base::showpoint;
+
+      // Insert first digit.
+      if (!__buf[0])
+	*__sbuf++ = __digits[0];	// Insert implicit 0.
+      else
+	*__sbuf++= __digits[(*__bp++) - '0'];
+
+      // Insert decimal point.
+      // Showpoint always prints point.  Otherwise when prec > 0, there must
+      // be digits to print or forced padding.
+      if ((__prec && (*__bp || __pad)) || __showpoint)
+	*__sbuf++= __dpchar;
+
+
+      // Insert digits after decimal point
+      for (;__prec && *__bp; __prec--)
+	*__sbuf++ = __digits[*__bp++ - '0'];
+      if (__pad)
+	while (__prec--)
+	  *__sbuf++ = __digits[0];
+
+      // Insert exponent symbol and sign
+      *__sbuf++ = (__flags & ios_base::uppercase) ? __atoms[__num_base::_S_oE]
+	: __atoms[__num_base::_S_oe];
+
+      __decp--;
+      if (__decp < 0) 
+	{
+	  *__sbuf++ = __atoms[__num_base::_S_ominus];
+	  __decp = -__decp;
+	}
+      else 
+	*__sbuf++ = __atoms[__num_base::_S_oplus];
+
+
+      // Write the exponent.  Large enough for ieee quad (15 bit exponent)
+      char __tbuf[6];
+      char *__tb = __tbuf + 5;
+      *__tb-- = 0;
+      while (__decp) 
+	{
+	  *__tb-- = '0' + (__decp % 10);
+	  __decp /= 10;
+	}
+
+      // We need at least 2 digits in exponent.  Pad if it's missing
+      while (__tb - __tbuf > 2) 
+	*__tb-- = '0';
+      while (*++__tb)
+	*__sbuf++ = __digits[*__tb - '0'];
+      return __sbuf - __sb;
+    }
+
+  template<typename _Tp>
+    char* __dtoa(char*, _Tp, int, int, int, bool, int*);
+
+  template<typename _Tp>
+    int __float_to_char_sprintf(char*, _Tp, int, int, ios_base::fmtflags);
+
+  // Convert floating point value to string.
+  //
+  // Takes a floating point value and converts it to a string using dtoa,
+  // falling back to sprintf if needed.
+  //
+  // buf is the wide char buffer to print into, bufsize is the length of buf.
+  //
+  // atoms contains numeric and exponent wide chars to use for the result.
+  // dpchar is the decimal point char to use.
+  //
+  // mode is 0 for %f style, 1 for %e style, and 2 for %g style printing.
+  // v is the value to print, and exp is the exponent estimate from
+  // dec_exponent.
+  //
+  // prec is the precision to use for printing, and is interpreted according
+  // to the mode.
+  //
+  // flags contains the relevant printing flags: showpoint, showpos, and
+  // uppercase.
+  template<typename _CharT, typename _ValueT>
+    inline int
+    __float_to_char(_CharT* __buf, int __bufsize, const _CharT* __atoms,
+		    _CharT __dpchar, int __mode, _ValueT __v, int __exp,
+		    int __prec, ios_base::fmtflags __flags)
+    {
+      char __lbuf[__bufsize];
+      int __decp;
+
+      // Insert sign.
+      int __len = 0;
+      if (__v < 0)
+	{
+	  *__buf++ = __atoms[__num_base::_S_ominus];
+	  __len = 1;
+	}
+      else if (__flags & ios_base::showpos)
+	{
+	  __len = 1;
+	  *__buf++ = __atoms[__num_base::_S_oplus];
+	}
+      
+      // Dispatch number conversion to the correct function.
+      if (__mode == 0)
+	{
+	  if (!__dtoa(__lbuf, __v, __exp, 3, __prec, false, &__decp))
+	    __len += __float_to_char_sprintf(__lbuf, __v, __mode, __prec,
+					     __flags);
+	  else
+	    __len +=__float_to_char_fixed(__buf, __atoms, __dpchar,
+					  &__lbuf[0], __decp, __prec, __flags,
+					  true);
+	}
+      else if (__mode == 1)
+	{
+	  if (!__dtoa(__lbuf, __v, __exp, 2, __prec + 1, false, &__decp))
+	    __len += __float_to_char_sprintf(__lbuf, __v, __mode, __prec,
+					     __flags);
+	  else
+	    __len += __float_to_char_scientific(__buf, __atoms, __dpchar,
+						&__lbuf[0], __decp, __prec,
+					      __flags, true);
+	}
+      else
+	{
+	  if (!__dtoa(__lbuf, __v, __exp, 2, __prec, true, &__decp))
+	    __len += __float_to_char_sprintf(__lbuf, __v, __mode, __prec,
+					     __flags);
+	  else
+	    {
+	      if (__decp <= -4 || __decp > __prec)
+		__len += __float_to_char_scientific(__buf, __atoms, __dpchar,
+						    &__lbuf[0], __decp,
+						    __prec - 1, __flags,
+						    false);
+	      else
+		__len += __float_to_char_fixed(__buf, __atoms, __dpchar, 
+					       &__lbuf[0], __decp,
+					       __prec - __decp, __flags,
+					       false);
+	    }
+	}
+      return __len;
+    }
+
+
+  template<typename _Tp>
+    int
+    __dec_exponent(_Tp d, int& special, bool& sign);
+
+
   template<typename _CharT, typename _OutIter>
     template<typename _ValueT>
       _OutIter
@@ -1071,58 +1309,81 @@
 	// Long enough for the max format spec.
 	char __fbuf[16];
 
-#ifdef _GLIBCXX_USE_C99
-	// First try a buffer perhaps big enough (most probably sufficient
-	// for non-ios_base::fixed outputs)
-	int __cs_size = __max_digits * 3;
-	char* __cs = static_cast<char*>(__builtin_alloca(__cs_size));
-
-	__num_base::_S_format_float(__io, __fbuf, __mod);
-	__len = std::__convert_from_v(__cs, __cs_size, __fbuf, __v,
-				      _S_get_c_locale(), __prec);
-
-	// If the buffer was not large enough, try again with the correct size.
-	if (__len >= __cs_size)
+	int __special;
+	bool __sign;
+	_CharT* __ws;
+	const int __exponent = __dec_exponent(__v, __special, __sign);
+	if (__special)
 	  {
-	    __cs_size = __len + 1;
-	    __cs = static_cast<char*>(__builtin_alloca(__cs_size));
-	    __len = std::__convert_from_v(__cs, __cs_size, __fbuf, __v,
-					  _S_get_c_locale(), __prec);
+	    const ctype<_CharT>& __ctype = use_facet<ctype<_CharT> >(__loc);
+	    const char* __cs;
+	    __ws = static_cast<_CharT*>(__builtin_alloca(sizeof(_CharT) * 4));
+	    if (__special == 1)
+	      {
+		if (__sign)
+		  __cs = __io.flags() & ios_base::uppercase ? "-INF" : "-inf";
+		else if (__io.flags() & ios_base::showpos)
+		  __cs = __io.flags() & ios_base::uppercase ? "+INF" : "+inf";
+		else
+		  __cs = __io.flags() & ios_base::uppercase ? "INF" : "inf";
+	      }
+	    else
+	      // special == 2
+	      __cs = __io.flags() & ios_base::uppercase ? "NAN" : "nan";
+	    __len = strlen(__cs);
+	    __ctype.widen(__cs, __cs + __len, __ws);
+      	  }
+	else
+	  {
+	    // Fixed needs space to hold all digits to left of decimal
+	    // point, plus up to __prec digits to the right.  The
+	    // other formats need __prec digits plus sign, decimal
+	    // point, exponent char and sign, and exponent itself.
+	    int __decp;
+	    int __cs_size;
+	    int __mode;
+	    const int __maxexp10 = numeric_limits<_ValueT>::max_exponent10;
+
+	    if (__io.flags() & ios_base::fixed)
+	      {
+		// %f style
+		// sign + (exp + 1) digits + decpoint + prec digits
+		__cs_size = (__exponent > 0 ? __exponent : 0) + 3 + __prec;
+		__mode = 0;
+	      }
+	    else if (__io.flags() & ios_base::scientific)
+	      {
+		// %e style
+		// sign + digit + decpt + precision + e + sign + explen
+		const int __max_exp_len =
+		  int(__builtin_log10(__maxexp10)) + 1;
+		__cs_size = __prec + 5 + __max_exp_len;
+		__mode = 1;
+	      }
+	    else
+	      {
+		// %g style 
+		// sign + digit + decpt + prec + e + sign + exp
+		// When exp >-4 || < prec, use %f style.  We could need 2*prec
+		// digits.
+		const int __max_exp_len =
+		  int(__builtin_log10(__maxexp10)) + 1;
+		if (!__prec) 
+		  __prec = 1;
+		__cs_size = __prec * 2 + 5 + __max_exp_len;
+		__mode = 2;
+	      }
+      
+	    __ws = static_cast<_CharT*>(__builtin_alloca(sizeof(_CharT)
+							 * __cs_size));
+	    __len = __float_to_char(__ws, __cs_size, &__lc->_M_atoms_out[0],
+				    __lc->_M_decimal_point, __mode, __v,
+				    __exponent, __prec, __io.flags());
 	  }
-#else
-	// Consider the possibility of long ios_base::fixed outputs
-	const bool __fixed = __io.flags() & ios_base::fixed;
-	const int __max_exp = numeric_limits<_ValueT>::max_exponent10;
-
-	// The size of the output string is computed as follows.
-	// ios_base::fixed outputs may need up to __max_exp + 1 chars
-	// for the integer part + __prec chars for the fractional part
-	// + 3 chars for sign, decimal point, '\0'. On the other hand,
-	// for non-fixed outputs __max_digits * 2 + __prec chars are
-	// largely sufficient.
-	const int __cs_size = __fixed ? __max_exp + __prec + 4
-	                              : __max_digits * 2 + __prec;
-	char* __cs = static_cast<char*>(__builtin_alloca(__cs_size));
-
-	__num_base::_S_format_float(__io, __fbuf, __mod);
-	__len = std::__convert_from_v(__cs, 0, __fbuf, __v,
-				      _S_get_c_locale(), __prec);
-#endif
 
       // [22.2.2.2.2] Stage 2, convert to char_type, using correct
       // numpunct.decimal_point() values for '.' and adding grouping.
-      const ctype<_CharT>& __ctype = use_facet<ctype<_CharT> >(__loc);
-
-      _CharT* __ws = static_cast<_CharT*>(__builtin_alloca(sizeof(_CharT)
-							   * __len));
-      __ctype.widen(__cs, __cs + __len, __ws);
-
-      // Replace decimal point.
-      const _CharT __cdec = __ctype.widen('.');
-      const _CharT __dec = __lc->_M_decimal_point;
-      const _CharT* __p = char_traits<_CharT>::find(__ws, __len, __cdec);
-      if (__p)
-	__ws[__p - __ws] = __dec;
+      // (The conversion happened in __float_to_char(), above.)
 
       // Add grouping, if necessary.
       if (__lc->_M_use_grouping)
@@ -1131,6 +1392,8 @@
 	  // number of digits, but no more.
 	  _CharT* __ws2 = static_cast<_CharT*>(__builtin_alloca(sizeof(_CharT)
 								* __len * 2));
+	  const _CharT* __p = 
+	    char_traits<_CharT>::find(__ws, __len, __lc->_M_decimal_point);
 	  _M_group_float(__lc->_M_grouping, __lc->_M_grouping_size,
 			 __lc->_M_thousands_sep, __p, __ws2, __ws, __len);
 	  __ws = __ws2;
Index: src/Makefile.am
===================================================================
RCS file: /cvs/gcc/gcc/libstdc++-v3/src/Makefile.am,v
retrieving revision 1.144
diff -u -r1.144 Makefile.am
--- src/Makefile.am	16 Apr 2004 19:04:05 -0000	1.144
+++ src/Makefile.am	31 Aug 2004 00:43:49 -0000
@@ -102,6 +102,7 @@
 	ctype.cc \
 	debug.cc \
 	debug_list.cc \
+	floatconv.cc \
 	functexcept.cc \
 	globals_locale.cc \
 	globals_io.cc \
Index: src/Makefile.in
===================================================================
RCS file: /cvs/gcc/gcc/libstdc++-v3/src/Makefile.in,v
retrieving revision 1.208
diff -u -r1.208 Makefile.in
--- src/Makefile.in	23 Aug 2004 10:18:30 -0000	1.208
+++ src/Makefile.in	31 Aug 2004 00:43:50 -0000
@@ -65,7 +65,7 @@
 	numeric_members.lo time_members.lo
 am__objects_2 = basic_file.lo c++locale.lo
 am__objects_3 = allocator.lo codecvt.lo complex_io.lo ctype.lo \
-	debug.lo debug_list.lo functexcept.lo globals_locale.lo \
+	debug.lo debug_list.lo floatconv.lo functexcept.lo globals_locale.lo \
 	globals_io.lo ios.lo ios_failure.lo ios_init.lo ios_locale.lo \
 	limits.lo list.lo locale.lo locale_init.lo locale_facets.lo \
 	localename.lo stdexcept.lo strstream.lo tree.lo \
@@ -309,6 +309,7 @@
 	ctype.cc \
 	debug.cc \
 	debug_list.cc \
+	floatconv.cc \
 	functexcept.cc \
 	globals_locale.cc \
 	globals_io.cc \
Index: src/locale-inst.cc
===================================================================
RCS file: /cvs/gcc/gcc/libstdc++-v3/src/locale-inst.cc,v
retrieving revision 1.50
diff -u -r1.50 locale-inst.cc
--- src/locale-inst.cc	27 Feb 2004 10:12:01 -0000	1.50
+++ src/locale-inst.cc	31 Aug 2004 00:43:50 -0000
@@ -32,6 +32,12 @@
 // ISO C++ 14882: 22.1  Locales
 //
 
+// This file instantiates locale-related classes and functions for char and
+// wchar_t.  Use C in place of the template char type.  wlocale-inst.cc
+// changes the definition of C to reinstantiate everything with wchar_t.  For
+// locale-related functions that don't depend on the char type, see
+// locale-misc-inst.cc.
+
 #include <locale>
 
 // Instantiation configuration.
@@ -313,5 +319,17 @@
     __int_to_char(C*, unsigned long long, const C*, 
 		  ios_base::fmtflags, bool);
 #endif
+
+
+  template
+    int
+    __float_to_char_fixed(C*, const C*, C, const char*, int, int,
+			  ios_base::fmtflags, bool);
+
+  template
+    int
+    __float_to_char_scientific(C*, const C*, C, const char*, int, int,
+			       ios_base::fmtflags, bool);
+
 } // namespace std
--- /dev/null	2004-08-19 18:00:15.000000000 -0400
+++ src/floatconv.cc	2004-08-30 21:31:17.000000000 -0400
@@ -0,0 +1,704 @@
+// Numeric printing support -*- C++ -*-
+
+// Copyright (C) 1993, 1994, 2004
+// Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 2, 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 General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING.  If not, write to the Free
+// Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+// USA.
+
+// As a special exception, you may use this file as part of a free software
+// library without restriction.  Specifically, if other files instantiate
+// templates or use macros or inline functions from this file, or you compile
+// this file and link it with other files to produce an executable, this
+// file does not by itself cause the resulting executable to be covered by
+// the GNU General Public License.  This exception does not however
+// invalidate any other reasons why the executable file might be covered by
+// the GNU General Public License.
+
+// Original copyright below
+
+/* 
+Copyright (C) 1993, 1994 Free Software Foundation
+
+This file is part of the GNU IO Library.  This library is free
+software; you can redistribute it and/or modify it under the
+terms of the GNU General Public License as published by the
+Free Software Foundation; either version 2, 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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this library; see the file COPYING.  If not, write to the Free
+Software Foundation, 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+As a special exception, if you link this library with files
+compiled with a GNU compiler to produce an executable, this does not cause
+the resulting executable to be covered by the GNU General Public License.
+This exception does not however invalidate any other reasons why
+the executable file might be covered by the GNU General Public License. */
+
+// #include <libioP.h>
+/****************************************************************
+ *
+ * The author of this software is David M. Gay.
+ *
+ * Copyright (c) 1991 by AT&T.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ *
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHOR NOR AT&T MAKES ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ *
+ ***************************************************************/
+
+/* Some cleaning up by Per Bothner, bothner@cygnus.com, 1992, 1993.
+   Re-written to not need static variables
+   (except result, result_k, HIWORD, LOWORD). */
+
+/* Note that the checking of _DOUBLE_IS_32BITS is for use with the
+   cross targets that employ the newlib ieeefp.h header.  -- brendan */
+
+/* Please send bug reports to
+        David M. Gay
+        AT&T Bell Laboratories, Room 2C-463
+        600 Mountain Avenue
+        Murray Hill, NJ 07974-2070
+        U.S.A.
+        dmg@research.att.com or research!dmg
+ */
+
+/* strtod for IEEE-, VAX-, and IBM-arithmetic machines.
+ *
+ * This strtod returns a nearest machine number to the input decimal
+ * string (or sets errno to ERANGE).  With IEEE arithmetic, ties are
+ * broken by the IEEE round-even rule.  Otherwise ties are broken by
+ * biased rounding (add half and chop).
+ *
+ * Inspired loosely by William D. Clinger's paper "How to Read Floating
+ * Point Numbers Accurately" [Proc. ACM SIGPLAN '90, pp. 92-101].
+ *
+ * Modifications:
+ *
+ *      1. We only require IEEE, IBM, or VAX double-precision
+ *              arithmetic (not IEEE double-extended).
+ *      2. We get by with floating-point arithmetic in a case that
+ *              Clinger missed -- when we're computing d * 10^n
+ *              for a small integer d and the integer n is not too
+ *              much larger than 22 (the maximum integer k for which
+ *              we can represent 10^k exactly), we may be able to
+ *              compute (d*10^k) * 10^(e-k) with just one roundoff.
+ *      3. Rather than a bit-at-a-time adjustment of the binary
+ *              result in the hard case, we use floating-point
+ *              arithmetic to determine the adjustment to within
+ *              one bit; only in really hard cases do we need to
+ *              compute a second residual.
+ *      4. Because of 3., we don't need a large table of powers of 10
+ *              for ten-to-e (just some small tables, e.g. of 10^k
+ *              for 0 <= k <= 22).
+ */
+
+/*
+ * #define IEEE for IEEE floating-point arithmetic
+ * #define IBM for IBM mainframe-style floating-point arithmetic.
+ * #define VAX for VAX-style floating-point arithmetic.
+ * #define Unsigned_Shifts if >> does treats its left operand as unsigned.
+ * #define Check_FLT_ROUNDS if FLT_ROUNDS can assume the values 2 or 3.
+ */
+
+#include <clocale>
+#include <cstring>
+#include <cmath>		// log, floor, frexp
+#include <limits>
+#include <ios>
+
+namespace std
+{
+
+// Bletch = (highest power of 2 < DBL_MAX_10_EXP) / 16
+// Quick_max = floor((mant_size-1)*log(FLT_RADIX)/log(10) - 1)
+// Int_max = floor(mant_size*log(FLT_RADIX)/log(10) - 1)
+template<typename _T>
+struct __fptype
+{
+  typedef std::numeric_limits<_T> nld;
+
+  static const int mant_size = nld::digits;
+  static const int Bletch;
+  static const int Quick_max;
+  static const int Int_max;
+};
+
+template<typename _T>
+const int __fptype<_T>::Bletch
+  = (1 << __builtin_ffs(__fptype::nld::max_exponent10)) / 16;
+
+template<typename _T>
+const int __fptype<_T>::Quick_max
+  = int(floor((__fptype::nld::digits-1) *
+	      log(double(__fptype::nld::radix))/log(10) - 1));
+
+// For secondary fast codepath
+template<typename _T>
+const int __fptype<_T>::Int_max
+  = int(floor(__fptype::nld::digits * log(__fptype::nld::radix)/log(10) - 1));
+
+template
+struct __fptype<double>;
+
+template
+struct __fptype<long double>;
+
+
+// Ten_pmax = floor(mant_size*log(2)/log(5))
+// IEEE & IBM double is 22
+// VAX double is 24
+// IEEE quad is 48
+// This is the number of elements in tens[] to use.
+static const int __Ten_pmax = int(__fptype<double>::mant_size * log(2)/log(5));
+
+// Enough entries to handle long double with 112-bit mantissa
+static const double
+__tens[] = {
+                1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9,
+                1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
+                1e20, 1e21, 1e22, 1e23, 1e24, 1e25, 1e26, 1e27, 1e28, 1e29,
+                1e30, 1e31, 1e32, 1e33, 1e34, 1e35, 1e36, 1e37, 1e38, 1e39,
+                1e40, 1e41, 1e42, 1e43, 1e44, 1e45, 1e46, 1e47, 1e48, 1e49
+};
+static const double __bigtens[] = { 1e16, 1e32, 1e64, 1e128, 1e256, 1e512,
+				  1e1024, 1e2048, 1e4096 };
+// 5 for ieee double
+// 3 for s370 double
+// 2 for vax double
+static const int __n_bigtens =
+  int(log(std::numeric_limits<double>::max_exponent10)/log(2.0)) - 3;
+
+static const long double __bigtensl[] =
+  { 1e16L, 1e32L, 1e64L, 1e128L, 1e256L, 1e512L, 1e1024L, 1e2048L, 1e4096L,
+    1e8192L, 1e16384L };
+
+static const int __n_bigtensl =
+  int(log(std::numeric_limits<long double>::max_exponent10)/log(2.0)) - 3;
+
+
+template<typename _T>
+_T __bigten(int i);
+
+template<>
+static inline double
+__bigten<double>(int i) { return __bigtens[i]; }
+
+template<>
+static inline long double
+__bigten<long double>(int i) { return __bigtensl[i]; }
+
+template<typename _T>
+_T __maxbigten();
+
+template<>
+static inline double __maxbigten<double>()
+{ return __bigtens[__n_bigtens-1]; }
+
+template<>
+static inline long double __maxbigten<long double>()
+{ return __bigtensl[__n_bigtensl-1]; }
+
+
+
+// dec_exponent performs the first portion of the conversion to a string.  It
+// computes an upper bound on the decimal exponent that can be used to size a
+// buffer to print into.  It must be passed back into dtoa.
+//
+// This will only be used for double and long double
+template<typename _T>
+int
+__dec_exponent(_T d, int &special, bool &sign)
+{
+  /*     Arguments ndigits, decpt, sign are similar to those
+	 of ecvt and fcvt; trailing zeros are suppressed from
+	 the returned string.  If not null, *rve is set to point
+	 to the end of the return value.  If d is +-Infinity or NaN,
+	 then *decpt is set to 9999.
+
+	 special returns true if the number is nan, infinity, or 0.
+	 type contains -1 for nan, 0 for 0, and 1 for infinity.  This allows
+	 the calling routine to handle the special cases.
+  */
+
+  int i;
+  int k;			// Holds base 10 exponent
+
+  _T d2, ds;
+
+  // Handle NAN.
+  if (__builtin_isnan(d))
+    {
+      special = 2;
+      return 3;
+    }
+
+  // Save sign value into user var
+  if (__builtin_signbit(d)) {
+    // set sign for everything, including 0's and NaNs
+    sign = true;
+    d = -d;		// Clear sign bit.
+  }
+  else
+    sign = false;
+
+  // Handle IEEE and VAX infinity and nan.  Apparently IBM has none.
+  if (__builtin_isinf(d))
+    {
+      special = 1;
+      return 8;
+    }
+
+  special = 0;
+
+  if (d == 0.0) return 0;
+
+  // Extract base-2 exponent and normalized fraction.
+  d2 = frexp(d, &i);
+  
+  // Compute integer base 10 exponent into k
+
+  /* log(x)       ~=~ log(1.5) + (x-1.5)/1.5
+   * log10(x)      =  log(x) / log(10)
+   *              ~=~ log(1.5)/log(10) + (x-1.5)/(1.5*log(10))
+   * log10(d) = i*log(2)/log(10) + log10(d2)
+   *
+   * This suggests computing an approximation k to log10(d) by
+   *
+   * k = i*0.301029995663981
+   *      + ( (d2-1.5)*0.289529654602168 + 0.176091259055681 );
+   *
+   * We want k to be too large rather than too small.
+   * The error in the first-order Taylor series approximation
+   * is in our favor, so we just round up the constant enough
+   * to compensate for any error in the multiplication of
+   * (i) by 0.301029995663981; since |i| <= 1077,
+   * and 1077 * 0.30103 * 2^-52 ~=~ 7.2e-14,
+   * adding 1e-13 to the constant term more than suffices.
+   * Hence we adjust the constant term to 0.1760912590558.
+   * (We could get a more accurate k by invoking log10,
+   *  but this is probably not worthwhile.)
+   *
+   * (From Richard Henderson)
+   * The comment should be updated for long double.  I guess the
+   * overestimate is still ok, since while |i| <= 16881 is much
+   * larger, 2^-64 and 2^-113 are much smaller.
+   */
+  ds = (d2-1.5)*0.289529654602168 + 0.1760912590558 + i*0.301029995663981;
+  k = (int)ds;
+  if (ds < 0. && ds != k)
+    k--;    // want k = floor(ds)
+  return k;
+}
+
+template
+int
+__dec_exponent<double>(double d, int &special, bool &sign);
+
+template
+int
+__dec_exponent<long double>(long double d, int &special, bool &sign);
+
+/* dtoa for IEEE arithmetic (dmg): convert double to ASCII string.
+ *
+ * Inspired by "How to Print Floating-Point Numbers Accurately" by
+ * Guy L. Steele, Jr. and Jon L. White [Proc. ACM SIGPLAN '90, pp. 92-101].
+ *
+ * Modifications:
+ *      1. Rather than iterating, we use a simple numeric overestimate
+ *         to determine k = floor(log10(d)).  We scale relevant
+ *         quantities using O(log2(k)) rather than O(k) multiplications.
+ *      2. For some modes > 2 (corresponding to ecvt and fcvt), we don't
+ *         try to generate digits strictly left to right.  Instead, we
+ *         compute with fewer bits and propagate the carry if necessary
+ *         when rounding the final digit up.  This is often faster.
+ *      3. Under the assumption that input will be rounded nearest,
+ *         mode 0 renders 1e23 as 1e23 rather than 9.999999999999999e22.
+ *         That is, we allow equality in stopping tests when the
+ *         round-nearest rule will give the same floating-point value
+ *         as would satisfaction of the stopping test with strict
+ *         inequality.
+ *      4. We remove common factors of powers of 2 from relevant
+ *         quantities.
+ *      5. When converting floating-point integers less than 1e16,
+ *         we use floating-point arithmetic rather than resorting
+ *         to multiple-precision integers.
+ *      6. When asked to produce fewer than 15 digits, we first try
+ *         to get by with floating-point arithmetic; we resort to
+ *         multiple-precision integer arithmetic only if we cannot
+ *         guarantee that the floating-point calculation has given
+ *         the correctly rounded result.  For k requested digits and
+ *         "uniformly" distributed input, the probability is
+ *         something like 10^(k-15) that we must resort to the long
+ *         calculation.
+ */
+
+// Slow method requiring big int math has been removed, returning 0.  This is
+// because printf was about 30% faster by comparison.
+//
+// buf is a buffer whose size was computed using dec_exponent(), so as to be
+// large enough to hold the number if needed.
+//
+// exponent is the value computed by dec_exponent
+//
+// The mode is as described below, but only 2 and 3 are still used.
+//
+// Mode 2 ndigits gives the total significant digits to print.
+// Mode 3 ndigits gives the total digits after the decimal point to print.
+//
+// If trim is true, trailing zeros will be trimmed off.
+//
+// decpt returns the actual decimal point location.  It will often be
+// exponent, but may be adjusted in some cases.
+//
+// The return value is 0 if the routine gave up, otherwise it is buf.
+
+template<typename _T>
+char *
+__dtoa(char* buf, _T d, int exponent, int mode, int ndigits, bool trim,
+       int *decpt)
+{
+  /*     Arguments ndigits, decpt, sign are similar to those of ecvt and fcvt;
+	 trailing zeros are suppressed from the returned string.  If d is
+	 +-Infinity or NaN, then *decpt is set to 9999.
+
+	 mode:
+	 0 ==> shortest string that yields d when read in
+	       and rounded to nearest.
+	 1 ==> like 0, but with Steele & White stopping rule;
+	       e.g. with IEEE P754 arithmetic , mode 0 gives
+	       1e23 whereas mode 1 gives 9.999999999999999e22.
+	 2 ==> max(1,ndigits) significant digits.  This gives a
+	       return value similar to that of ecvt, except
+	       that trailing zeros are suppressed.
+	 3 ==> through ndigits past the decimal point.  This
+	       gives a return value similar to that from fcvt,
+	       except that trailing zeros are suppressed, and
+	       ndigits can be negative.
+
+	 4-9 should give the same return values as 2-3, i.e.,
+	       4 <= mode <= 9 ==> same return as mode
+	       2 + (mode & 1).  These modes are mainly for
+	       debugging; often they run slower but sometimes
+	       faster than modes 2-3.
+	 4,5,8,9 ==> left-to-right digit generation.
+	 6-9 ==> don't try fast floating-point estimate
+	 	(if applicable).
+
+	 Values of mode other than 0-9 are treated as mode 0.
+
+	 Sufficient space is allocated to the return value
+	 to hold the suppressed trailing zeros.
+  */
+
+  int i, ilim=0, ilim1=0;
+  int k;			// Holds base 10 exponent
+  int k_check;
+  int L;
+  _T ds, eps;
+  char *s, *s0, *stmp;
+
+  // Handle 0 for all cases.
+  if (d == 0.0) {
+    *decpt = 1;
+    buf[0] = '0';
+    buf[1] = 0;
+    return buf;
+  }
+
+  // Clear the sign.
+  d = fabs(d);
+
+  // Extract base-2 exponent.
+  frexp(d, &i);
+
+  // k is an upper bound on the integer base 10 exponent of d.  We may need to
+  // tweak it a little.
+  k = exponent;
+  k_check = 1;
+  if (k >= 0 && k < __Ten_pmax) {
+    if (d < __tens[k])
+      k--;
+    k_check = 0;
+  }
+
+  if (mode == 2) {
+    if (ndigits <= 0)
+      ndigits = 1;
+    ilim = ilim1 = i = ndigits;
+  }
+  else {
+    i = ndigits + k + 1;
+    ilim = i;
+    ilim1 = i - 1;
+    if (i <= 0)
+      i = 1;
+  }
+  // i is now an upper bound of the number of digits to generate.
+
+  s = s0 = buf;
+
+  if (ilim >= 0 && ilim <= __fptype<_T>::Quick_max) {
+
+    // Try to get by with floating-point arithmetic.
+
+    _T d2 = d;
+    int i = 0;
+    int k0 = k;
+    int ilim0 = ilim;
+    int ieps = 2; // conservative
+    int j1;
+    int j;
+
+    if (k > 0) {
+      ds = __tens[k&0xf];		// 1e0 -> 1e15
+      j = k >> 4;
+      if (j & __fptype<_T>::Bletch) {
+	// I've never seen this code hit in .5 million random numbers
+	// prevent overflows
+	// n_bigtens
+	j &= __fptype<_T>::Bletch - 1;
+	d /= __maxbigten<_T>();
+	ieps++;
+      }
+      for(; j; j >>= 1, i++)
+	if (j & 1) {
+	  ieps++;
+	  ds *= __bigten<_T>(i);
+	}
+      d /= ds;
+    }
+    else if ((j1 = -k)) {
+      d *= __tens[j1 & 0xf];
+      for(j = j1 >> 4; j; j >>= 1, i++)
+	if (j & 1) {
+	  ieps++;
+	  d *= __bigten<_T>(i);
+	}
+    }
+    if (k_check && d < 1. && ilim > 0) {
+      if (ilim1 <= 0)
+	goto fast_failed;
+      ilim = ilim1;
+      k--;
+      d *= 10.;
+      ieps++;
+    }
+    eps = ieps*d + 7.;
+    static const _T e2m = ldexp(2, -(__fptype<_T>::mant_size-1));
+    eps *= e2m;
+    if (ilim == 0) {
+      d -= 5.;
+      if (d > eps)
+	goto one_digit;
+      if (d < -eps)
+	goto no_digits;
+      goto fast_failed;
+    }
+
+    // Generate ilim digits, then fix them up.
+    eps *= __tens[ilim-1];
+    for(i = 1;; i++, d *= 10.) {
+      L = (int)d;
+      d -= L;
+      *s++ = '0' + (int)L;
+      if (i == ilim) {
+	if (d > 0.5 + eps)
+	  goto bump_up;
+	else if (d < 0.5 - eps) {
+	  // Strips off trailing 0's
+	  if (trim) {
+	    while(*--s == '0');
+	    s++;
+	  }
+	  goto ret;
+	}
+	// else fast approach failed
+	break;
+      }
+    }
+  fast_failed:
+    s = s0;
+    d = d2;
+    k = k0;
+    ilim = ilim0;
+  }
+
+  // Do we have a "small" integer?
+
+  if (k <= __fptype<_T>::Int_max && floor(d) == d) {
+    // Yes.
+    ds = __tens[k];
+    if (ndigits < 0 && ilim <= 0) {
+      if (ilim < 0 || d <= 5*ds)
+	goto no_digits;
+      goto one_digit;
+    }
+    for(i = 1;; i++) {
+      L = (int)(d / ds);
+      d -= L*ds;
+
+      if (std::numeric_limits<_T>::round_style
+	  == std::round_toward_infinity ||
+	  std::numeric_limits<_T>::round_style
+	  == std::round_toward_neg_infinity)
+	{
+	// If FLT_ROUNDS == 2, L will usually be high by 1
+	if (d < 0) {
+	  L--;
+	  d += ds;
+	}
+      }
+
+      *s++ = '0' + (int)L;
+      if (i == ilim) {
+	d += d;
+	if (d > ds || (d == ds && L & 1)) {
+	bump_up:
+	  stmp = s;
+	  while( *--s == '9' ) {
+	    *s='0';
+	    if (s == s0) {
+	      k++;
+	      *s = '0';
+	      break;
+	    }
+	  }
+	  ++*s++;
+	  if (!trim) s = stmp;
+	}
+	break;
+      }
+      if (!(d *= 10.))
+	break;
+    }
+    goto ret;
+  }
+
+  return 0;			// XXX bail out to avoid mp int calcs
+
+  /* no digits, fcvt style */
+ no_digits:
+  k = -1 - ndigits;
+  goto ret;
+
+ one_digit:
+  *s++ = '1';
+  k++;
+  goto ret;
+
+ ret:
+  *s = 0;
+  *decpt = k + 1;
+  return s0;
+}
+
+template
+char *
+__dtoa<double>(char* buf, double d, int exponent, int mode, int ndigits,
+	       bool trim, int *decpt);
+
+template
+char *
+__dtoa<long double>(char* buf, long double d, int exponent, int mode,
+		    int ndigits, bool trim, int *decpt);
+
+
+// Call to detect if we're working on double or long double.
+template<typename _T>
+static inline bool __is_long();
+
+template<>
+static inline bool __is_long<double>() { return false; }
+
+template<>
+static inline bool __is_long<long double>() { return true; }
+
+// The last three are in flags.
+template<typename _T>
+int
+__float_to_char_sprintf(char* buf, _T d, int mode, int prec,
+			ios_base::fmtflags flags)
+{
+  static const char lmodes[] = "feg";
+  static const char umodes[] = "FEG";
+
+  bool point = flags & ios_base::showpoint;
+  bool pos = flags & ios_base::showpos;
+  bool up = flags & ios_base::uppercase;
+
+  // Build the format string
+  char fmt[8] = "%";
+  char* fp = &fmt[1];
+  if (pos) *fp++ = '+';
+  if (point) *fp++ = '#';
+  *fp++ = '.';
+  *fp++ = '*';
+
+  if (__is_long<_T>())
+    *fp++ = 'L';
+
+  if (up)
+    *fp++ = umodes[mode];
+  else
+    *fp++ = lmodes[mode];
+
+  *fp=0;
+
+  // This is safe since the buffer will be big enough.
+  sprintf(&buf[0], fmt, prec, d);
+
+  // Standard says we need to print as if in the C locale.  The only
+  // difference that may show up is that the point char can be different.
+  // Change it to '.'.
+  char dp = localeconv()->decimal_point[0];
+  char* bp = buf;
+  for (; *bp; bp++)
+    if (*bp == dp) {
+      *bp = '.';
+      break;
+    }
+  // Return the full length of the string
+  int len = bp - buf;
+  len += strlen(bp);
+  return len;
+}
+
+template
+int
+__float_to_char_sprintf<double>(char* buf, double d, int mode, int prec,
+				ios_base::fmtflags flags);
+
+template
+int
+__float_to_char_sprintf<long double>(char* buf, long double d, int mode,
+				     int prec, ios_base::fmtflags flags);
+
+}


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]