bug-bash
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Bash floating-point printf floating-point precision improvement


From: Paul Eggert
Subject: Bash floating-point printf floating-point precision improvement
Date: Mon, 19 Nov 2001 15:46:23 -0800 (PST)

Configuration Information [Automatically generated, do not change]:
Machine: i686
OS: linux-gnu
Compiler: gcc
Compilation CFLAGS:  -DPROGRAM='bash' -DCONF_HOSTTYPE='i686' 
-DCONF_OSTYPE='linux-gnu' -DCONF_MACHTYPE='i686-pc-linux-gnu' 
-DCONF_VENDOR='pc' -DSHELL -DHAVE_CONFIG_H   -I.  -I.. -I../include -I../lib -g 
-O2
uname output: Linux dum.twinsun.com 2.2.18ss.e820-bda652a #4 SMP Tue Jun 5 
11:24:08 PDT 2001 i686 unknown
Machine Type: i686-pc-linux-gnu

Bash Version: 2.05a
Patch Level: 0
Release Status: release

Description:
        Bash's printf builtin always uses 'long int' to print integers
        (or 'intmax_t', the widest available integer type, assuming my
        earlier patches of today are installed).
        
        For consistency, Bash's printf should also use the widest
        available floating-point type to format floating-point
        numbers.  This will yield more precise results, and it won't
        cost much CPU as the code is so rarely used.  Also, this
        simplifies the code a bit.

Repeat-By:
        $ printf '%.17g\n' 0.1
        0.10000000000000001
        $ printf '%g\n' 1e-321
        9.98013e-322
        $ printf '%g\n' 1e400
        bash: printf: warning: 1e400: Numerical result out of range
        inf

        With the patch below, the outputs are "0.1", "1e-321", and
        "1e+400" respectively; these outputs are more precise and are
        more what a naive user would expect.

Fix:

2001-11-19  Paul Eggert  <address@hidden>

        * builtins/printf.def (getdouble, getldouble): Remove, replacing with:
        (getfloatmax): New function.
        (FLOATMAX_CONV): New macro.
        (floatmax): New type decl.
        (printf_builtin): Always convert floating-point values using
        the widest available floating-point type.  This makes the code
        more consistent with integer printing, and generates more precise
        results on many platforms.

===================================================================
RCS file: builtins/printf.def,v
retrieving revision 2.5.1.4.0.2
retrieving revision 2.5.1.4.0.3
diff -pc -r2.5.1.4.0.2 -r2.5.1.4.0.3
*** builtins/printf.def 2001/11/19 18:06:21     2.5.1.4.0.2
--- builtins/printf.def 2001/11/19 23:40:57     2.5.1.4.0.3
*************** static char *getstr __P((void));
*** 112,121 ****
  static int  getint __P((void));
  static intmax_t getintmax __P((void));
  static uintmax_t getuintmax __P((void));
- static double getdouble __P((void));
  #if defined (HAVE_LONG_DOUBLE) && HAVE_DECL_STRTOLD
! static long double getldouble __P((void));
  #endif
  static int asciicode __P((void));
  
  static WORD_LIST *garglist;
--- 112,127 ----
  static int  getint __P((void));
  static intmax_t getintmax __P((void));
  static uintmax_t getuintmax __P((void));
  #if defined (HAVE_LONG_DOUBLE) && HAVE_DECL_STRTOLD
! typedef long double floatmax;
! #  define FLOATMAX_CONV "L"
! #  define string_to_floatmax strtold
! #else
! typedef double floatmax;
! #  define FLOATMAX_CONV ""
! #  define string_to_floatmax strtod
  #endif
+ static floatmax getfloatmax __P((void));
  static int asciicode __P((void));
  
  static WORD_LIST *garglist;
*************** printf_builtin (list)
*** 386,409 ****
  #endif
              {
                char *f;
! #if defined (HAVE_LONG_DOUBLE) && HAVE_DECL_STRTOLD
!               if (thisch == 'L')
!                 {
!                   long double p;
! 
!                   p = getldouble ();
!                   f = mklong (start, "L", 1);
!                   PF (f, p);
!                 }
!               else
! #endif
!                 {
!                   double p;
  
!                   p = getdouble ();
!                   f = mklong (start, "", 0);
!                   PF (f, p);
!                 }
                break;
              }
  
--- 392,402 ----
  #endif
              {
                char *f;
!               floatmax p;
  
!               p = getfloatmax ();
!               f = mklong (start, FLOATMAX_CONV, sizeof FLOATMAX_CONV - 1);
!               PF (f, p);
                break;
              }
  
*************** getuintmax ()
*** 812,821 ****
    return (ret);
  }
  
! static double
! getdouble ()
  {
!   double ret;
    char *ep;
  
    if (garglist == 0)
--- 805,814 ----
    return (ret);
  }
  
! static floatmax
! getfloatmax ()
  {
!   floatmax ret;
    char *ep;
  
    if (garglist == 0)
*************** getdouble ()
*** 825,861 ****
      return asciicode ();
  
    errno = 0;
!   ret = strtod (garglist->word->word, &ep);
! 
!   if (*ep)
!     {
!       builtin_error ("%s: invalid number", garglist->word->word);
!       /* Same thing about POSIX.2 conversion error requirements. */
!       ret = 0;
!       conversion_error = 1;
!     }
!   else if (errno == ERANGE)
!     builtin_error ("warning: %s: %s", garglist->word->word, strerror(ERANGE));
! 
!   garglist = garglist->next;
!   return (ret);
! }
! 
! #if defined (HAVE_LONG_DOUBLE) && HAVE_DECL_STRTOLD
! static long double
! getldouble ()
! {
!   long double ret;
!   char *ep;
! 
!   if (garglist == 0)
!     return (0);
! 
!   if (garglist->word->word[0] == '\'' || garglist->word->word[0] == '"')
!     return (asciicode ());
! 
!   errno = 0;
!   ret = strtold (garglist->word->word, &ep);
  
    if (*ep)
      {
--- 818,824 ----
      return asciicode ();
  
    errno = 0;
!   ret = string_to_floatmax (garglist->word->word, &ep);
  
    if (*ep)
      {
*************** getldouble ()
*** 870,876 ****
    garglist = garglist->next;
    return (ret);
  }
- #endif /* HAVE_LONG_DOUBLE && HAVE_DECL_STRTOLD */
  
  /* NO check is needed for garglist here. */
  static int
--- 833,838 ----



reply via email to

[Prev in Thread] Current Thread [Next in Thread]