NetBSD-Bugs archive

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

Re: lib/59811: strpct round-off error



The following reply was made to PR lib/59811; it has been noted by GNATS.

From: Robert Elz <kre%munnari.OZ.AU@localhost>
To: gnats-bugs%netbsd.org@localhost
Cc: 
Subject: Re: lib/59811: strpct round-off error
Date: Wed, 03 Dec 2025 12:12:13 +0700

 I now have (what I believe to be) working code to fix this.
 
 I am including 2 potential manual pages - please tell me which one
 you prefer (as in, which style of the fix -- in the actual text, I
 worked on the 2nd version below after the first, and in the process
 improved its wording - I think - if the first one is the preferred
 method, I'll redo the wording of it to be more like the second).
 
 I also "enhanced" slightly the OP's mwe.c test program, to use in testing
 this - mwe2 takes args, with options for the rounding mode desired and the
 number of fractional digits (also the buffer size, but that's just for
 testing perverse stupid usages), its usage is:
 
 Usage: mwe2 [-f digits] [-r mode] [-s bufsize] [ numerator [ denominator ] ]
 
 And using that with the original numerator & denominator (as given in this
 PR, which remain the defaults) and with -f1 (to get one decimal place),
 with the 3 rounding modes, I see:
 
 jacaranda$ ./mwe2 -f1 -r0
  Current: 66.6
 Expected: 66.6
 jacaranda$ ./mwe2 -f1 -r1
  Current: 66.7
 Expected: 66.7
 jacaranda$ ./mwe2 -f1 -r2
  Current: 66.7
 Expected: 66.7
 
 Note that the "expected" value is now computed (in mwe2.c, using floating
 point) to be consistent with the -f and -r args given.
 
 In case it happens not to be obvious by its very nature, -r1 always
 produces the same output as either -r0 or -r2 (which depends upon the
 data).
 
 Then also:
 jacaranda$ ./mwe2 -f2 -r1
  Current: 66.67
 Expected: 66.67
 jacaranda$ ./mwe2 -f2 -r0
  Current: 66.66
 Expected: 66.66
 jacaranda$ ./mwe2 -f12 -r1
  Current: 66.666666682064
 Expected: 66.666666682064
 jacaranda$ ./mwe2 -f11 -r1
  Current: 66.66666668206
 Expected: 66.66666668206
 jacaranda$ ./mwe2 -f11 -r2
  Current: 66.66666668207
 Expected: 66.66666668207
 
 The current implementation uses the first of the man pages below, but
 changing it to be the second will take only about a minute, so ignore that.
 
 I believe these do need a method to specify which rounding style to use,
 how to do that is the difference between the two man pages below.  It would
 be easy to just not do that, and simply pick one of them (and since df's
 usage is fairly simple, it could adjust the args to force the rounding it
 is supposed to use, if the one and only style implemented were not the one
 that it requires.)
 
 These also presume that any changes implemented now will get pulled up
 to NetBSD 11 - that is by no means certain.  If it doesn't happen, the
 reference to .Nx 11.0 can easily turn into .Nx 12.0.
 
 I have tried to avoid inventing names for anything, as much as is possible,
 so please feel free to make suggestions for unnamed objects, or alterations
 where I invented one.
 
 So, here are the two possible manual pages (sorry, no visible markup here,
 which makes it harder to read, I know - it exists though):
 
 STRPCT(3)                  Library Functions Manual                  STRPCT(3)
 
 NAME
      strpct, strspct - decimal percent formatters
 
 LIBRARY
      System Utilities Library (libutil, -lutil)
 
 SYNOPSIS
      #include <util.h>
 
      char *
      strpct(char *buf, size_t bufsiz, uintmax_t numerator,
          uintmax_t denominator, size_t precision);
 
      char *
      strspct(char *buf, size_t bufsiz, intmax_t numerator,
          intmax_t denominator, size_t precision);
 
      int
      strpct_round(int mode);
 
 DESCRIPTION
      The strpct() function formats the fraction represented by numerator and
      denominator into a percentage representation with given number of digits
      of precision without using floating point arithmetic.
 
      The strspct() function is identical to strpct() except uses signed values
      for the numerator and denominator, and so can return a result with a
      leading minus sign.
 
      The result is rounded according to the current rounding mode, which for
      historical reasons defaults to rounding towards zero (that is truncating,
      as would be done using integer arithmetic).  This can be altered using
      strpct_round().  The mode specifies which of 3 implemented rounding modes
      to use.  If mode is given as zero, the default of rounding towards 0 (no
      rounding) is used.  If mode is given as 1, the least significant digit of
      the result returned will be rounded to the nearest correct value.  Values
      mid way between one digit and the next round away from zero.  If mode is
      given as 2, the least significant digit of the result will be incremented
      if there were any less significant non-zero digits not included in the
      result -- that is, rounded away from zero.  If mode is given as INT_MAX
      or -INT_MAX, the rounding will be set as for mode 0.  Those values are
      defined to allow for future implementation (unlikely to ever happen) of
      routing towards infinity (INT_MAX) or negative infinity (-INT_MAX).  Any
      other value for mode is simply igored, and the rounding mode is not
      changed.
 
 RETURN VALUES
      strpct() and strspct() always return a pointer to a NUL-terminated
      (unless bufsiz is 0) formatted string which is placed in buf.
      strpct_round() returns the rounding mode that was in use before the call.
      To obtain this information without altering the rounding mode, call
      strpct_round() with a mode of -1.
 
 EXAMPLES
            strpct(buf, sizeof(buf), 1, 16, 3);
            => "6.250"
            strpct(buf, sizeof(buf), 1, 2, 0);
            => "50"
 
 HISTORY
      strpct() was originally implemented in csh(1) for NetBSD 1.3.  It printed
      into a static buffer, was not locale aware, handled unsigned long
      numbers, and printed a "%" at the end of the number.  Other programs such
      as df(1) and time(1) started using it.  strpct() and strspct() appeared
      separately in libutil for NetBSD 6.0.
 
      strpct_round() appeared in NetBSD 11.0.
 
 AUTHORS
      Erik E. Fair <fair%NetBSD.org@localhost>
      Roland Illig <rillig%NetBSD.org@localhost>
 
 NetBSD 11.99.4                 December 3, 2025                 NetBSD 11.99.4
 
 
 or, an alternative:
 
 STRPCT(3)                  Library Functions Manual                  STRPCT(3)
 
 NAME
      strpct, strspct, strpct_r, strspct_r - decimal percent formatters
 
 LIBRARY
      System Utilities Library (libutil, -lutil)
 
 SYNOPSIS
      #include <util.h>
 
      char *
      strpct(char *buf, size_t bufsiz, uintmax_t numerator,
          uintmax_t denominator, size_t precision);
 
      char *
      strpct_r(char *buf, size_t bufsiz, uintmax_t numerator,
          uintmax_t denominator, size_t precision, int rounding_mode);
 
      char *
      strspct(char *buf, size_t bufsiz, intmax_t numerator,
          intmax_t denominator, size_t precision);
 
      char *
      strspct_r(char *buf, size_t bufsiz, intmax_t numerator,
          intmax_t denominator, size_t precision, int rounding_mode);
 
 DESCRIPTION
      The strpct_r() function formats the fraction represented by numerator and
      denominator into a percentage representation with given number of digits
      of precision without using floating point arithmetic.
 
      The result is stored in the buf in which a maximum of bufsiz - 1
      meaningful bytes can be stored, and is rounded according to the
      rounding_mode.  The current locale's radix character, typically period
      (`.') or comma (`,'), is inserted before fractional digits if the
      precision is greater than 0.  If rounding_mode is given as zero, rounding
      towards 0 (truncating rather than rounding) is used.  If rounding_mode is
      given as 1, the result returned will be rounded to the nearest correct
      value.  Actual values exactly mid way between one representable value and
      the next round away from zero.  If rounding_mode is given as 2, the
      result will be rounded, if needed, to the next value further away from
      zero.
 
      The strspct_r() function is identical to strpct_r() except uses signed
      values for the numerator and denominator, and so can return a result with
      a leading minus sign.
 
      The strpct() and strspct() functions are identical to strpct_r() and
      strspct_r() respectively, with the rounding_mode specified as zero.
 
 RETURN VALUES
      strpct_r(), strspct_r(), strpct() and strspct() always return a pointer
      to a NUL-terminated (unless bufsiz is 0) formatted string which is placed
      in buf.
 
 EXAMPLES
            strpct(buf, sizeof(buf), 1, 16, 3);
            => "6.250"
            strpct(buf, sizeof(buf), 1, 2, 0);
            => "50"
 
 HISTORY
      strpct() was originally implemented in csh(1) for NetBSD 1.3.  It printed
      into a static buffer, was not locale aware, handled unsigned long
      numbers, and printed a "%" at the end of the number.  Other programs such
      as df(1) and time(1) started using it.  strpct() and strspct() appeared
      separately in libutil for NetBSD 6.0.
 
      strpct_r() and strspct_r() appeared in NetBSD 11.0.
 
 AUTHORS
      Erik E. Fair <fair%NetBSD.org@localhost>
      Roland Illig <rillig%NetBSD.org@localhost>
 
 BUGS
      If the supplied buffer size is insufficient for the result (including a
      terminating nul (`\0') character), the result will be silently truncated
      with only the most significant digits included, the last of which will be
      rounded using the requested method.  This is not useful.  If the buffer
      space is exhausted part way through a locale's (multi-byte) radix
      character, even more bizarre behaviour is to be expected.  Always provide
      a buffer bigger than can possibly be needed.
 
      Rather than causing an abnormal process termination, as it arguably
      should, a denominator specified as zero will be treated as if it were
      one.
 
 NetBSD 11.99.4                 December 3, 2025                 NetBSD 11.99.4
 
 


Home | Main Index | Thread Index | Old Index