Commit f797ae74 authored by Bruno Haible's avatar Bruno Haible

Fix rounding when a precision is given.

parent 2e895677
2008-04-19 Bruno Haible <bruno@clisp.org>
Fix rounding when a precision is given.
* lib/vasnprintf.c (is_borderline): New function.
(VASNPRINTF): For %e and %g, consider replacing the digits 10....0 with
9...9x.
* tests/test-vasnprintf-posix.c (test_function): Test rounding with %f,
%e, %g.
* tests/test-vasprintf-posix.c (test_function): Likewise.
* tests/test-snprintf-posix.h (test_function): Likewise.
* tests/test-sprintf-posix.h (test_function): Likewise.
* tests/test-fprintf-posix.h (test_function): Test rounding with %f.
* tests/test-printf-posix.h (test_function): Likewise.
* tests/test-printf-posix.output: Update.
Reported by John Darrington <john@darrington.wattle.id.au> via
Ben Pfaff <blp@cs.stanford.edu>.
2008-04-18 Simon Josefsson <simon@josefsson.org>
* doc/posix-functions/strftime.texi (strftime): Clarify platform.
......@@ -1404,6 +1404,20 @@ floorlog10 (double x)
# endif
/* Tests whether a string of digits consists of exactly PRECISION zeroes and
a single '1' digit. */
static int
is_borderline (const char *digits, size_t precision)
{
for (; precision > 0; precision--, digits++)
if (*digits != '0')
return 0;
if (*digits != '1')
return 0;
digits++;
return *digits == '\0';
}
#endif
DCHAR_T *
......@@ -2853,8 +2867,32 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
exponent += 1;
adjusted = 1;
}
/* Here ndigits = precision+1. */
if (is_borderline (digits, precision))
{
/* Maybe the exponent guess was too high
and a smaller exponent can be reached
by turning a 10...0 into 9...9x. */
char *digits2 =
scale10_round_decimal_long_double (arg,
(int)precision - exponent + 1);
if (digits2 == NULL)
{
free (digits);
END_LONG_DOUBLE_ROUNDING ();
goto out_of_memory;
}
if (strlen (digits2) == precision + 1)
{
free (digits);
digits = digits2;
exponent -= 1;
}
else
free (digits2);
}
/* Here ndigits = precision+1. */
*p++ = digits[--ndigits];
if ((flags & FLAG_ALT) || precision > 0)
{
......@@ -2966,6 +3004,30 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
adjusted = 1;
}
/* Here ndigits = precision. */
if (is_borderline (digits, precision - 1))
{
/* Maybe the exponent guess was too high
and a smaller exponent can be reached
by turning a 10...0 into 9...9x. */
char *digits2 =
scale10_round_decimal_long_double (arg,
(int)(precision - 1) - exponent + 1);
if (digits2 == NULL)
{
free (digits);
END_LONG_DOUBLE_ROUNDING ();
goto out_of_memory;
}
if (strlen (digits2) == precision)
{
free (digits);
digits = digits2;
exponent -= 1;
}
else
free (digits2);
}
/* Here ndigits = precision. */
/* Determine the number of trailing zeroes
that have to be dropped. */
......@@ -3206,8 +3268,31 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
exponent += 1;
adjusted = 1;
}
/* Here ndigits = precision+1. */
if (is_borderline (digits, precision))
{
/* Maybe the exponent guess was too high
and a smaller exponent can be reached
by turning a 10...0 into 9...9x. */
char *digits2 =
scale10_round_decimal_double (arg,
(int)precision - exponent + 1);
if (digits2 == NULL)
{
free (digits);
goto out_of_memory;
}
if (strlen (digits2) == precision + 1)
{
free (digits);
digits = digits2;
exponent -= 1;
}
else
free (digits2);
}
/* Here ndigits = precision+1. */
*p++ = digits[--ndigits];
if ((flags & FLAG_ALT) || precision > 0)
{
......@@ -3332,6 +3417,29 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
adjusted = 1;
}
/* Here ndigits = precision. */
if (is_borderline (digits, precision - 1))
{
/* Maybe the exponent guess was too high
and a smaller exponent can be reached
by turning a 10...0 into 9...9x. */
char *digits2 =
scale10_round_decimal_double (arg,
(int)(precision - 1) - exponent + 1);
if (digits2 == NULL)
{
free (digits);
goto out_of_memory;
}
if (strlen (digits2) == precision)
{
free (digits);
digits = digits2;
exponent -= 1;
}
else
free (digits2);
}
/* Here ndigits = precision. */
/* Determine the number of trailing zeroes
that have to be dropped. */
......
/* Test of POSIX compatible vsprintf() and sprintf() functions.
Copyright (C) 2007 Free Software Foundation, Inc.
Copyright (C) 2007-2008 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
......@@ -65,6 +65,12 @@ test_function (int (*my_fprintf) (FILE *, const char *, ...))
/* Precision. */
my_fprintf (stdout, "%.f %d\n", 1234.0, 33, 44, 55);
/* Precision with no rounding. */
my_fprintf (stdout, "%.2f %d\n", 999.95, 33, 44, 55);
/* Precision with rounding. */
my_fprintf (stdout, "%.2f %d\n", 999.996, 33, 44, 55);
/* A positive number. */
my_fprintf (stdout, "%Lf %d\n", 12.75L, 33, 44, 55);
......@@ -83,6 +89,12 @@ test_function (int (*my_fprintf) (FILE *, const char *, ...))
/* Precision. */
my_fprintf (stdout, "%.Lf %d\n", 1234.0L, 33, 44, 55);
/* Precision with no rounding. */
my_fprintf (stdout, "%.2Lf %d\n", 999.95L, 33, 44, 55);
/* Precision with rounding. */
my_fprintf (stdout, "%.2Lf %d\n", 999.996L, 33, 44, 55);
/* Test the support of the %F format directive. */
/* A positive number. */
......@@ -103,6 +115,12 @@ test_function (int (*my_fprintf) (FILE *, const char *, ...))
/* Precision. */
my_fprintf (stdout, "%.F %d\n", 1234.0, 33, 44, 55);
/* Precision with no rounding. */
my_fprintf (stdout, "%.2F %d\n", 999.95, 33, 44, 55);
/* Precision with rounding. */
my_fprintf (stdout, "%.2F %d\n", 999.996, 33, 44, 55);
/* A positive number. */
my_fprintf (stdout, "%LF %d\n", 12.75L, 33, 44, 55);
......@@ -121,6 +139,12 @@ test_function (int (*my_fprintf) (FILE *, const char *, ...))
/* Precision. */
my_fprintf (stdout, "%.LF %d\n", 1234.0L, 33, 44, 55);
/* Precision with no rounding. */
my_fprintf (stdout, "%.2LF %d\n", 999.95L, 33, 44, 55);
/* Precision with rounding. */
my_fprintf (stdout, "%.2LF %d\n", 999.996L, 33, 44, 55);
/* Test the support of the POSIX/XSI format strings with positions. */
my_fprintf (stdout, "%2$d %1$d\n", 33, 55);
......
/* Test of POSIX compatible vsprintf() and sprintf() functions.
Copyright (C) 2007 Free Software Foundation, Inc.
Copyright (C) 2007-2008 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
......@@ -67,6 +67,12 @@ test_function (int (*my_printf) (const char *, ...))
/* Precision. */
my_printf ("%.f %d\n", 1234.0, 33, 44, 55);
/* Precision with no rounding. */
my_printf ("%.2f %d\n", 999.95, 33, 44, 55);
/* Precision with rounding. */
my_printf ("%.2f %d\n", 999.996, 33, 44, 55);
/* A positive number. */
my_printf ("%Lf %d\n", 12.75L, 33, 44, 55);
......@@ -85,6 +91,12 @@ test_function (int (*my_printf) (const char *, ...))
/* Precision. */
my_printf ("%.Lf %d\n", 1234.0L, 33, 44, 55);
/* Precision with no rounding. */
my_printf ("%.2Lf %d\n", 999.95L, 33, 44, 55);
/* Precision with rounding. */
my_printf ("%.2Lf %d\n", 999.996L, 33, 44, 55);
/* Test the support of the %F format directive. */
/* A positive number. */
......@@ -105,6 +117,12 @@ test_function (int (*my_printf) (const char *, ...))
/* Precision. */
my_printf ("%.F %d\n", 1234.0, 33, 44, 55);
/* Precision with no rounding. */
my_printf ("%.2F %d\n", 999.95, 33, 44, 55);
/* Precision with rounding. */
my_printf ("%.2F %d\n", 999.996, 33, 44, 55);
/* A positive number. */
my_printf ("%LF %d\n", 12.75L, 33, 44, 55);
......@@ -123,6 +141,12 @@ test_function (int (*my_printf) (const char *, ...))
/* Precision. */
my_printf ("%.LF %d\n", 1234.0L, 33, 44, 55);
/* Precision with no rounding. */
my_printf ("%.2LF %d\n", 999.95L, 33, 44, 55);
/* Precision with rounding. */
my_printf ("%.2LF %d\n", 999.996L, 33, 44, 55);
/* Test the support of the POSIX/XSI format strings with positions. */
my_printf ("%2$d %1$d\n", 33, 55);
......
......@@ -11,22 +11,30 @@ inf 33
0.000000 33
00001234.000000 33
1234 33
999.95 33
1000.00 33
12.750000 33
1234567.000000 33
-0.031250 33
0.000000 33
00001234.000000 33
1234 33
999.95 33
1000.00 33
12.750000 33
1234567.000000 33
-0.031250 33
0.000000 33
00001234.000000 33
1234 33
999.95 33
1000.00 33
12.750000 33
1234567.000000 33
-0.031250 33
0.000000 33
00001234.000000 33
1234 33
999.95 33
1000.00 33
55 33
......@@ -1004,6 +1004,22 @@ test_function (int (*my_snprintf) (char *, size_t, const char *, ...))
ASSERT (retval == strlen (result));
}
{ /* Precision with no rounding. */
char result[100];
int retval =
my_snprintf (result, sizeof (result), "%.2f %d", 999.951, 33, 44, 55);
ASSERT (strcmp (result, "999.95 33") == 0);
ASSERT (retval == strlen (result));
}
{ /* Precision with rounding. */
char result[100];
int retval =
my_snprintf (result, sizeof (result), "%.2f %d", 999.996, 33, 44, 55);
ASSERT (strcmp (result, "1000.00 33") == 0);
ASSERT (retval == strlen (result));
}
{ /* A positive number. */
char result[100];
int retval =
......@@ -1338,6 +1354,22 @@ test_function (int (*my_snprintf) (char *, size_t, const char *, ...))
ASSERT (retval == strlen (result));
}
{ /* Precision with no rounding. */
char result[100];
int retval =
my_snprintf (result, sizeof (result), "%.2Lf %d", 999.951L, 33, 44, 55);
ASSERT (strcmp (result, "999.95 33") == 0);
ASSERT (retval == strlen (result));
}
{ /* Precision with rounding. */
char result[100];
int retval =
my_snprintf (result, sizeof (result), "%.2Lf %d", 999.996L, 33, 44, 55);
ASSERT (strcmp (result, "1000.00 33") == 0);
ASSERT (retval == strlen (result));
}
/* Test the support of the %F format directive. */
{ /* A positive number. */
......@@ -1434,6 +1466,22 @@ test_function (int (*my_snprintf) (char *, size_t, const char *, ...))
ASSERT (retval == strlen (result));
}
{ /* Precision with no rounding. */
char result[100];
int retval =
my_snprintf (result, sizeof (result), "%.2F %d", 999.951, 33, 44, 55);
ASSERT (strcmp (result, "999.95 33") == 0);
ASSERT (retval == strlen (result));
}
{ /* Precision with rounding. */
char result[100];
int retval =
my_snprintf (result, sizeof (result), "%.2F %d", 999.996, 33, 44, 55);
ASSERT (strcmp (result, "1000.00 33") == 0);
ASSERT (retval == strlen (result));
}
{ /* A positive number. */
char result[100];
int retval =
......@@ -1528,6 +1576,22 @@ test_function (int (*my_snprintf) (char *, size_t, const char *, ...))
ASSERT (retval == strlen (result));
}
{ /* Precision with no rounding. */
char result[100];
int retval =
my_snprintf (result, sizeof (result), "%.2LF %d", 999.951L, 33, 44, 55);
ASSERT (strcmp (result, "999.95 33") == 0);
ASSERT (retval == strlen (result));
}
{ /* Precision with rounding. */
char result[100];
int retval =
my_snprintf (result, sizeof (result), "%.2LF %d", 999.996L, 33, 44, 55);
ASSERT (strcmp (result, "1000.00 33") == 0);
ASSERT (retval == strlen (result));
}
/* Test the support of the %e format directive. */
{ /* A positive number. */
......@@ -1802,6 +1866,24 @@ test_function (int (*my_snprintf) (char *, size_t, const char *, ...))
ASSERT (retval == strlen (result));
}
{ /* Precision with no rounding. */
char result[100];
int retval =
my_snprintf (result, sizeof (result), "%.4e %d", 999.951, 33, 44, 55);
ASSERT (strcmp (result, "9.9995e+02 33") == 0
|| strcmp (result, "9.9995e+002 33") == 0);
ASSERT (retval == strlen (result));
}
{ /* Precision with rounding. */
char result[100];
int retval =
my_snprintf (result, sizeof (result), "%.4e %d", 999.996, 33, 44, 55);
ASSERT (strcmp (result, "1.0000e+03 33") == 0
|| strcmp (result, "1.0000e+003 33") == 0);
ASSERT (retval == strlen (result));
}
{ /* A positive number. */
char result[100];
int retval =
......@@ -2138,6 +2220,22 @@ test_function (int (*my_snprintf) (char *, size_t, const char *, ...))
ASSERT (retval == strlen (result));
}
{ /* Precision with no rounding. */
char result[100];
int retval =
my_snprintf (result, sizeof (result), "%.4Le %d", 999.951L, 33, 44, 55);
ASSERT (strcmp (result, "9.9995e+02 33") == 0);
ASSERT (retval == strlen (result));
}
{ /* Precision with rounding. */
char result[100];
int retval =
my_snprintf (result, sizeof (result), "%.4Le %d", 999.996L, 33, 44, 55);
ASSERT (strcmp (result, "1.0000e+03 33") == 0);
ASSERT (retval == strlen (result));
}
/* Test the support of the %g format directive. */
{ /* A positive number. */
......@@ -2401,6 +2499,22 @@ test_function (int (*my_snprintf) (char *, size_t, const char *, ...))
ASSERT (retval == strlen (result));
}
{ /* Precision with no rounding. */
char result[100];
int retval =
my_snprintf (result, sizeof (result), "%.5g %d", 999.951, 33, 44, 55);
ASSERT (strcmp (result, "999.95 33") == 0);
ASSERT (retval == strlen (result));
}
{ /* Precision with rounding. */
char result[100];
int retval =
my_snprintf (result, sizeof (result), "%.5g %d", 999.996, 33, 44, 55);
ASSERT (strcmp (result, "1000 33") == 0);
ASSERT (retval == strlen (result));
}
{ /* A positive number. */
char result[100];
int retval =
......@@ -2737,6 +2851,22 @@ test_function (int (*my_snprintf) (char *, size_t, const char *, ...))
ASSERT (retval == strlen (result));
}
{ /* Precision with no rounding. */
char result[100];
int retval =
my_snprintf (result, sizeof (result), "%.5Lg %d", 999.951L, 33, 44, 55);
ASSERT (strcmp (result, "999.95 33") == 0);
ASSERT (retval == strlen (result));
}
{ /* Precision with rounding. */
char result[100];
int retval =
my_snprintf (result, sizeof (result), "%.5Lg %d", 999.996L, 33, 44, 55);
ASSERT (strcmp (result, "1000 33") == 0);
ASSERT (retval == strlen (result));
}
/* Test the support of the %n format directive. */
{
......
......@@ -984,6 +984,22 @@ test_function (int (*my_sprintf) (char *, const char *, ...))
ASSERT (retval == strlen (result));
}
{ /* Precision with no rounding. */
char result[100];
int retval =
my_sprintf (result, "%.2f %d", 999.951, 33, 44, 55);
ASSERT (strcmp (result, "999.95 33") == 0);
ASSERT (retval == strlen (result));
}
{ /* Precision with rounding. */
char result[100];
int retval =
my_sprintf (result, "%.2f %d", 999.996, 33, 44, 55);
ASSERT (strcmp (result, "1000.00 33") == 0);
ASSERT (retval == strlen (result));
}
{ /* A positive number. */
char result[1000];
int retval =
......@@ -1312,6 +1328,22 @@ test_function (int (*my_sprintf) (char *, const char *, ...))
ASSERT (retval == strlen (result));
}
{ /* Precision with no rounding. */
char result[100];
int retval =
my_sprintf (result, "%.2Lf %d", 999.951L, 33, 44, 55);
ASSERT (strcmp (result, "999.95 33") == 0);
ASSERT (retval == strlen (result));
}
{ /* Precision with rounding. */
char result[100];
int retval =
my_sprintf (result, "%.2Lf %d", 999.996L, 33, 44, 55);
ASSERT (strcmp (result, "1000.00 33") == 0);
ASSERT (retval == strlen (result));
}
/* Test the support of the %F format directive. */
{ /* A positive number. */
......@@ -1408,6 +1440,22 @@ test_function (int (*my_sprintf) (char *, const char *, ...))
ASSERT (retval == strlen (result));
}
{ /* Precision with no rounding. */
char result[100];
int retval =
my_sprintf (result, "%.2F %d", 999.951, 33, 44, 55);
ASSERT (strcmp (result, "999.95 33") == 0);
ASSERT (retval == strlen (result));
}
{ /* Precision with rounding. */
char result[100];
int retval =
my_sprintf (result, "%.2F %d", 999.996, 33, 44, 55);
ASSERT (strcmp (result, "1000.00 33") == 0);
ASSERT (retval == strlen (result));
}
{ /* A positive number. */
char result[1000];
int retval =
......@@ -1502,6 +1550,22 @@ test_function (int (*my_sprintf) (char *, const char *, ...))
ASSERT (retval == strlen (result));
}
{ /* Precision with no rounding. */
char result[100];
int retval =
my_sprintf (result, "%.2LF %d", 999.951L, 33, 44, 55);
ASSERT (strcmp (result, "999.95 33") == 0);
ASSERT (retval == strlen (result));
}
{ /* Precision with rounding. */
char result[100];
int retval =
my_sprintf (result, "%.2LF %d", 999.996L, 33, 44, 55);
ASSERT (strcmp (result, "1000.00 33") == 0);
ASSERT (retval == strlen (result));
}
/* Test the support of the %e format directive. */
{ /* A positive number. */
......@@ -1776,6 +1840,24 @@ test_function (int (*my_sprintf) (char *, const char *, ...))
ASSERT (retval == strlen (result));
}
{ /* Precision with no rounding. */
char result[100];
int retval =
my_sprintf (result, "%.4e %d", 999.951, 33, 44, 55);
ASSERT (strcmp (result, "9.9995e+02 33") == 0
|| strcmp (result, "9.9995e+002 33") == 0);
ASSERT (retval == strlen (result));
}
{ /* Precision with rounding. */
char result[100];
int retval =
my_sprintf (result, "%.4e %d", 999.996, 33, 44, 55);
ASSERT (strcmp (result, "1.0000e+03 33") == 0
|| strcmp (result, "1.0000e+003 33") == 0);
ASSERT (retval == strlen (result));
}
{ /* A positive number. */
char result[1000];
int retval =
......@@ -2112,6 +2194,22 @@ test_function (int (*my_sprintf) (char *, const char *, ...))
ASSERT (retval == strlen (result));
}
{ /* Precision with no rounding. */
char result[100];
int retval =
my_sprintf (result, "%.4Le %d", 999.951L, 33, 44, 55);
ASSERT (strcmp (result, "9.9995e+02 33") == 0);
ASSERT (retval == strlen (result));
}
{ /* Precision with rounding. */
char result[100];
int retval =
my_sprintf (result, "%.4Le %d", 999.996L, 33, 44, 55);
ASSERT (strcmp (result, "1.0000e+03 33") == 0);
ASSERT (retval == strlen (result));
}
/* Test the support of the %g format directive. */
{ /* A positive number. */
......@@ -2375,6 +2473,22 @@ test_function (int (*my_sprintf) (char *, const char *, ...))
ASSERT (retval == strlen (result));
}
{ /* Precision with no rounding. */
char result[100];
int retval =
my_sprintf (result, "%.5g %d", 999.951, 33, 44, 55);
ASSERT (strcmp (result, "999.95 33") == 0);
ASSERT (retval == strlen (result));
}
{ /* Precision with rounding. */
char result[100];
int retval =
my_sprintf (result, "%.5g %d", 999.996, 33, 44, 55);
ASSERT (strcmp (result, "1000 33") == 0);
ASSERT (retval == strlen (result));
}
{ /* A positive number. */
char result[1000];
int retval =
......@@ -2711,6 +2825,22 @@ test_function (int (*my_sprintf) (char *, const char *, ...))
ASSERT (retval == strlen (result));
}
{ /* Precision with no rounding. */
char result[100];
int retval =
my_sprintf (result, "%.5Lg %d", 999.951L, 33, 44, 55);
ASSERT (strcmp (result, "999.95 33") == 0);
ASSERT (retval == strlen (result));
}
{ /* Precision with rounding. */
char result[100];
int retval =
my_sprintf (result, "%.5Lg %d", 999.996L, 33, 44, 55);
ASSERT (strcmp (result, "1000 33") == 0);
ASSERT (retval == strlen (result));
}
/* Test the support of the %n format directive. */
{
......
......@@ -1189,6 +1189,26 @@ test_function (char * (*my_asnprintf) (char *, size_t *, const char *, ...))
free (result);
}
{ /* Precision with no rounding. */
size_t length;
char *result =
my_asnprintf (NULL, &length, "%.2f %d", 999.951, 33, 44, 55);
ASSERT (result != NULL);
ASSERT (strcmp (result, "999.95 33") == 0);
ASSERT (length == strlen (result));
free (result);
}
{ /* Precision with rounding. */
size_t length;
char *result =
my_asnprintf (NULL, &length, "%.2f %d", 999.996, 33, 44, 55);
ASSERT (result != NULL);
ASSERT (strcmp (result, "1000.00 33") == 0);
ASSERT (length == strlen (result));
free (result);
}
{ /* A positive number. */
size_t length;
char *result =
......@@ -1569,6 +1589,26 @@ test_function (char * (*my_asnprintf) (char *, size_t *, const char *, ...))
free (result);
}
{ /* Precision with no rounding. */
size_t length;
char *result =
my_asnprintf (NULL, &length, "%.2Lf %d", 999.951L, 33, 44, 55);
ASSERT (result != NULL);
ASSERT (strcmp (result, "999.95 33") == 0);
ASSERT (length == strlen (result));
free (result);
}
{ /* Precision with rounding. */
size_t length;
char *result =
my_asnprintf (NULL, &length, "%.2Lf %d", 999.996L, 33, 44, 55);
ASSERT (result != NULL);
ASSERT (strcmp (result, "1000.00 33") == 0);
ASSERT (length == strlen (result));
free (result);
}
/* Test the support of the %F format directive. */
{ /* A positive number. */
......@@ -1687,6 +1727,26 @@ test_function (char * (*my_asnprintf) (char *, size_t *, const char *, ...))
free (result);
}
{ /* Precision with no rounding. */