summaryrefslogtreecommitdiff
path: root/stdio-common
diff options
context:
space:
mode:
Diffstat (limited to 'stdio-common')
-rw-r--r--stdio-common/Makefile2
-rw-r--r--stdio-common/tst-scanf-round.c51
-rw-r--r--stdio-common/vfscanf.c35
3 files changed, 71 insertions, 17 deletions
diff --git a/stdio-common/Makefile b/stdio-common/Makefile
index 9dfc115313..45038372ff 100644
--- a/stdio-common/Makefile
+++ b/stdio-common/Makefile
@@ -61,6 +61,7 @@ tests := tstscanf test_rdwr test-popen tstgetln test-fseek \
tst-printf-bz18872 tst-vfprintf-width-prec tst-fmemopen4 \
tst-vfprintf-user-type \
tst-vfprintf-mbs-prec \
+ tst-scanf-round \
test-srcs = tst-unbputc tst-printf
@@ -158,3 +159,4 @@ $(objpfx)tst-setvbuf1-cmp.out: tst-setvbuf1.expect $(objpfx)tst-setvbuf1.out
$(evaluate-test)
$(objpfx)tst-printf-round: $(libm)
+$(objpfx)tst-scanf-round: $(libm)
diff --git a/stdio-common/tst-scanf-round.c b/stdio-common/tst-scanf-round.c
new file mode 100644
index 0000000000..a2fb620abf
--- /dev/null
+++ b/stdio-common/tst-scanf-round.c
@@ -0,0 +1,51 @@
+/* Test for correct rounding of negative floating-point numbers by scanf
+ (bug 23280).
+ Copyright (C) 2018 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <http://www.gnu.org/licenses/>. */
+
+#include <fenv.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <support/check.h>
+
+static int
+do_test (void)
+{
+#ifdef FE_DOWNWARD
+ if (fesetround (FE_DOWNWARD) == 0)
+ {
+ double a = strtod ("-0.1", NULL);
+ double b = 0;
+ int r = sscanf ("-0.1", "%lf", &b);
+ TEST_VERIFY (r == 1);
+ TEST_VERIFY (a == b);
+ }
+#endif
+#ifdef FE_UPWARD
+ if (fesetround (FE_UPWARD) == 0)
+ {
+ double a = strtod ("-0.1", NULL);
+ double b = 0;
+ int r = sscanf ("-0.1", "%lf", &b);
+ TEST_VERIFY (r == 1);
+ TEST_VERIFY (a == b);
+ }
+#endif
+ return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/stdio-common/vfscanf.c b/stdio-common/vfscanf.c
index 3263268c7e..1ce836a324 100644
--- a/stdio-common/vfscanf.c
+++ b/stdio-common/vfscanf.c
@@ -292,7 +292,7 @@ _IO_vfscanf_internal (FILE *s, const char *format, va_list argptr,
/* Errno of last failed inchar call. */
int inchar_errno = 0;
/* Status for reading F-P nums. */
- char got_digit, got_dot, got_e, negative;
+ char got_digit, got_dot, got_e, got_sign;
/* If a [...] is a [^...]. */
CHAR_T not_in;
#define exp_char not_in
@@ -1914,20 +1914,19 @@ _IO_vfscanf_internal (FILE *s, const char *format, va_list argptr,
if (__glibc_unlikely (c == EOF))
input_error ();
- got_digit = got_dot = got_e = 0;
+ got_digit = got_dot = got_e = got_sign = 0;
/* Check for a sign. */
if (c == L_('-') || c == L_('+'))
{
- negative = c == L_('-');
+ got_sign = 1;
+ char_buffer_add (&charbuf, c);
if (__glibc_unlikely (width == 0 || inchar () == EOF))
/* EOF is only an input error before we read any chars. */
conv_error ();
if (width > 0)
--width;
}
- else
- negative = 0;
/* Take care for the special arguments "nan" and "inf". */
if (TOLOWER (c) == L_('n'))
@@ -2176,7 +2175,7 @@ _IO_vfscanf_internal (FILE *s, const char *format, va_list argptr,
digits with ASCII letters. */
&& !(flags & HEXA_FLOAT)
/* Minimum requirement. */
- && (char_buffer_size (&charbuf) == 0 || got_dot)
+ && (char_buffer_size (&charbuf) == got_sign || got_dot)
&& (map = __wctrans ("to_inpunct")) != NULL)
{
/* Reget the first character. */
@@ -2195,8 +2194,8 @@ _IO_vfscanf_internal (FILE *s, const char *format, va_list argptr,
for localized FP numbers, then we may have localized
digits. Note, we test GOT_DOT above. */
#ifdef COMPILE_WSCANF
- if (char_buffer_size (&charbuf) == 0
- || (char_buffer_size (&charbuf) == 1
+ if (char_buffer_size (&charbuf) == got_sign
+ || (char_buffer_size (&charbuf) == got_sign + 1
&& wcdigits[11] == decimal))
#else
char mbdigits[12][MB_LEN_MAX + 1];
@@ -2204,13 +2203,13 @@ _IO_vfscanf_internal (FILE *s, const char *format, va_list argptr,
mbstate_t state;
memset (&state, '\0', sizeof (state));
- bool match_so_far = char_buffer_size (&charbuf) == 0;
+ bool match_so_far = char_buffer_size (&charbuf) == got_sign;
size_t mblen = __wcrtomb (mbdigits[11], wcdigits[11], &state);
if (mblen != (size_t) -1)
{
mbdigits[11][mblen] = '\0';
match_so_far |=
- (char_buffer_size (&charbuf) == strlen (decimal)
+ (char_buffer_size (&charbuf) == strlen (decimal) + got_sign
&& strcmp (decimal, mbdigits[11]) == 0);
}
else
@@ -2220,7 +2219,8 @@ _IO_vfscanf_internal (FILE *s, const char *format, va_list argptr,
from a file. */
if (decimal_len <= MB_LEN_MAX)
{
- match_so_far |= char_buffer_size (&charbuf) == decimal_len;
+ match_so_far |= (char_buffer_size (&charbuf)
+ == decimal_len + got_sign);
memcpy (mbdigits[11], decimal, decimal_len + 1);
}
else
@@ -2284,7 +2284,7 @@ _IO_vfscanf_internal (FILE *s, const char *format, va_list argptr,
if (got_e && charbuf.current[-1] == exp_char
&& (c == L_('-') || c == L_('+')))
char_buffer_add (&charbuf, c);
- else if (char_buffer_size (&charbuf) > 0 && !got_e
+ else if (char_buffer_size (&charbuf) > got_sign && !got_e
&& (CHAR_T) TOLOWER (c) == exp_char)
{
char_buffer_add (&charbuf, exp_char);
@@ -2408,9 +2408,10 @@ _IO_vfscanf_internal (FILE *s, const char *format, va_list argptr,
/* Have we read any character? If we try to read a number
in hexadecimal notation and we have read only the `0x'
prefix this is an error. */
- if (__glibc_unlikely (char_buffer_size (&charbuf) == 0
+ if (__glibc_unlikely (char_buffer_size (&charbuf) == got_sign
|| ((flags & HEXA_FLOAT)
- && char_buffer_size (&charbuf) == 2)))
+ && (char_buffer_size (&charbuf)
+ == 2 + got_sign))))
conv_error ();
scan_float:
@@ -2427,21 +2428,21 @@ _IO_vfscanf_internal (FILE *s, const char *format, va_list argptr,
long double d = __strtold_internal
(char_buffer_start (&charbuf), &tw, flags & GROUP);
if (!(flags & SUPPRESS) && tw != char_buffer_start (&charbuf))
- *ARG (long double *) = negative ? -d : d;
+ *ARG (long double *) = d;
}
else if (flags & (LONG | LONGDBL))
{
double d = __strtod_internal
(char_buffer_start (&charbuf), &tw, flags & GROUP);
if (!(flags & SUPPRESS) && tw != char_buffer_start (&charbuf))
- *ARG (double *) = negative ? -d : d;
+ *ARG (double *) = d;
}
else
{
float d = __strtof_internal
(char_buffer_start (&charbuf), &tw, flags & GROUP);
if (!(flags & SUPPRESS) && tw != char_buffer_start (&charbuf))
- *ARG (float *) = negative ? -d : d;
+ *ARG (float *) = d;
}
if (__glibc_unlikely (tw == char_buffer_start (&charbuf)))