From a9f8e53a5b14ba481999ded036b025554ea06362 Mon Sep 17 00:00:00 2001 From: Joseph Myers Date: Mon, 24 Sep 2012 15:38:21 +0000 Subject: [PATCH] Make printf respect the rounding mode for hex output (bug 5044). --- ChangeLog | 13 +++++ NEWS | 12 ++-- stdio-common/printf_fphex.c | 40 ++++++++----- stdio-common/tst-printf-round.c | 124 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 170 insertions(+), 19 deletions(-) diff --git a/ChangeLog b/ChangeLog index a04e104..b8c492e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,16 @@ +2012-09-24 Joseph Myers + + [BZ #5044] + * stdio-common/printf_fphex.c: Include and + . + (__printf_fphex): Determine rounding using get_rounding_mode and + round_away. + * stdio-common/tst-printf-round.c (struct hex_test): New + structure. + (hex_tests): New variable. + (test_hex_in_one_mode): New function. + (do_test): Also run tests for hex float output. + 2012-09-21 Joseph Myers * libio/iopopen.c [_IO_HAVE_SYS_WAIT]: Make code unconditional. diff --git a/NEWS b/NEWS index d9dfd20..84c05d5 100644 --- a/NEWS +++ b/NEWS @@ -9,12 +9,12 @@ Version 2.17 * The following bugs are resolved with this release: - 1349, 3479, 5400, 6778, 6808, 9685, 9914, 10014, 10038, 11607, 13412, - 13542, 13717, 13696, 13939, 13966, 14042, 14090, 14166, 14150, 14151, - 14154, 14157, 14166, 14173, 14195, 14237, 14252, 14283, 14298, 14303, - 14307, 14328, 14331, 14336, 14337, 14347, 14349, 14459, 14476, 14505, - 14510, 14516, 14518, 14519, 14532, 14538, 14544, 14545, 14576, 14579, - 14583, 14587. + 1349, 3479, 5044, 5400, 6778, 6808, 9685, 9914, 10014, 10038, 11607, + 13412, 13542, 13717, 13696, 13939, 13966, 14042, 14090, 14166, 14150, + 14151, 14154, 14157, 14166, 14173, 14195, 14237, 14252, 14283, 14298, + 14303, 14307, 14328, 14331, 14336, 14337, 14347, 14349, 14459, 14476, + 14505, 14510, 14516, 14518, 14519, 14532, 14538, 14544, 14545, 14576, + 14579, 14583, 14587. * Support for STT_GNU_IFUNC symbols added for s390 and s390x. Optimized versions of memcpy, memset, and memcmp added for System z10 and diff --git a/stdio-common/printf_fphex.c b/stdio-common/printf_fphex.c index 1349145..2224196 100644 --- a/stdio-common/printf_fphex.c +++ b/stdio-common/printf_fphex.c @@ -28,6 +28,8 @@ #include <_itoa.h> #include <_itowa.h> #include +#include +#include /* #define NDEBUG 1*/ /* Undefine this for debugging assertions. */ #include @@ -343,21 +345,33 @@ __printf_fphex (FILE *fp, --numend; } + bool do_round_away = false; + + if (precision != -1 && precision < numend - numstr) + { + char last_digit = precision > 0 ? numstr[precision - 1] : leading; + char next_digit = numstr[precision]; + int last_digit_value = (last_digit >= 'A' && last_digit <= 'F' + ? last_digit - 'A' + 10 + : (last_digit >= 'a' && last_digit <= 'f' + ? last_digit - 'a' + 10 + : last_digit - '0')); + int next_digit_value = (next_digit >= 'A' && next_digit <= 'F' + ? next_digit - 'A' + 10 + : (next_digit >= 'a' && next_digit <= 'f' + ? next_digit - 'a' + 10 + : next_digit - '0')); + bool more_bits = ((next_digit_value & 7) != 0 + || precision + 1 < numend - numstr); + int rounding_mode = get_rounding_mode (); + do_round_away = round_away (negative, last_digit_value & 1, + next_digit_value >= 8, more_bits, + rounding_mode); + } + if (precision == -1) precision = numend - numstr; - else if (precision < numend - numstr - && (numstr[precision] > '8' - || (('A' < '0' || 'a' < '0') - && numstr[precision] < '0') - || (numstr[precision] == '8' - && (precision + 1 < numend - numstr - /* Round to even. */ - || (precision > 0 - && ((numstr[precision - 1] & 1) - ^ (isdigit (numstr[precision - 1]) == 0))) - || (precision == 0 - && ((leading & 1) - ^ (isdigit (leading) == 0))))))) + else if (do_round_away) { /* Round up. */ int cnt = precision; diff --git a/stdio-common/tst-printf-round.c b/stdio-common/tst-printf-round.c index 2bd4607..7cc19c5 100644 --- a/stdio-common/tst-printf-round.c +++ b/stdio-common/tst-printf-round.c @@ -68,6 +68,99 @@ test_dec_in_one_mode (double d, const char *fmt, const char *expected, } } +struct hex_test +{ + double d; + const char *fmt; + const char *rd[4], *rn[4], *rz[4], *ru[4]; +}; + +static const struct hex_test hex_tests[] = + { + { + 0x1.fffffp+4, "%.1a", + { "0x1.fp+4", "0x3.fp+3", "0x7.fp+2", "0xf.fp+1" }, + { "0x2.0p+4", "0x4.0p+3", "0x8.0p+2", "0x1.0p+5" }, + { "0x1.fp+4", "0x3.fp+3", "0x7.fp+2", "0xf.fp+1" }, + { "0x2.0p+4", "0x4.0p+3", "0x8.0p+2", "0x1.0p+5" } + }, + { + -0x1.fffffp+4, "%.1a", + { "-0x2.0p+4", "-0x4.0p+3", "-0x8.0p+2", "-0x1.0p+5" }, + { "-0x2.0p+4", "-0x4.0p+3", "-0x8.0p+2", "-0x1.0p+5" }, + { "-0x1.fp+4", "-0x3.fp+3", "-0x7.fp+2", "-0xf.fp+1" }, + { "-0x1.fp+4", "-0x3.fp+3", "-0x7.fp+2", "-0xf.fp+1" } + }, + { + 0x1.88p+4, "%.1a", + { "0x1.8p+4", "0x3.1p+3", "0x6.2p+2", "0xc.4p+1" }, + { "0x1.8p+4", "0x3.1p+3", "0x6.2p+2", "0xc.4p+1" }, + { "0x1.8p+4", "0x3.1p+3", "0x6.2p+2", "0xc.4p+1" }, + { "0x1.9p+4", "0x3.1p+3", "0x6.2p+2", "0xc.4p+1" } + }, + { + -0x1.88p+4, "%.1a", + { "-0x1.9p+4", "-0x3.1p+3", "-0x6.2p+2", "-0xc.4p+1" }, + { "-0x1.8p+4", "-0x3.1p+3", "-0x6.2p+2", "-0xc.4p+1" }, + { "-0x1.8p+4", "-0x3.1p+3", "-0x6.2p+2", "-0xc.4p+1" }, + { "-0x1.8p+4", "-0x3.1p+3", "-0x6.2p+2", "-0xc.4p+1" } + }, + { + 0x1.78p+4, "%.1a", + { "0x1.7p+4", "0x2.fp+3", "0x5.ep+2", "0xb.cp+1" }, + { "0x1.8p+4", "0x2.fp+3", "0x5.ep+2", "0xb.cp+1" }, + { "0x1.7p+4", "0x2.fp+3", "0x5.ep+2", "0xb.cp+1" }, + { "0x1.8p+4", "0x2.fp+3", "0x5.ep+2", "0xb.cp+1" } + }, + { + -0x1.78p+4, "%.1a", + { "-0x1.8p+4", "-0x2.fp+3", "-0x5.ep+2", "-0xb.cp+1" }, + { "-0x1.8p+4", "-0x2.fp+3", "-0x5.ep+2", "-0xb.cp+1" }, + { "-0x1.7p+4", "-0x2.fp+3", "-0x5.ep+2", "-0xb.cp+1" }, + { "-0x1.7p+4", "-0x2.fp+3", "-0x5.ep+2", "-0xb.cp+1" } + }, + { + 64.0 / 3.0, "%.1a", + { "0x1.5p+4", "0x2.ap+3", "0x5.5p+2", "0xa.ap+1" }, + { "0x1.5p+4", "0x2.bp+3", "0x5.5p+2", "0xa.bp+1" }, + { "0x1.5p+4", "0x2.ap+3", "0x5.5p+2", "0xa.ap+1" }, + { "0x1.6p+4", "0x2.bp+3", "0x5.6p+2", "0xa.bp+1" } + }, + { + -64.0 / 3.0, "%.1a", + { "-0x1.6p+4", "-0x2.bp+3", "-0x5.6p+2", "-0xa.bp+1" }, + { "-0x1.5p+4", "-0x2.bp+3", "-0x5.5p+2", "-0xa.bp+1" }, + { "-0x1.5p+4", "-0x2.ap+3", "-0x5.5p+2", "-0xa.ap+1" }, + { "-0x1.5p+4", "-0x2.ap+3", "-0x5.5p+2", "-0xa.ap+1" } + }, + }; + +static int +test_hex_in_one_mode (double d, const char *fmt, const char *expected[4], + const char *mode_name) +{ + char buf[100]; + int ret = snprintf (buf, sizeof buf, fmt, d); + if (ret <= 0 || ret >= (int) sizeof buf) + { + printf ("snprintf for %a returned %d\n", d, ret); + return 1; + } + if (strcmp (buf, expected[0]) == 0 + || strcmp (buf, expected[1]) == 0 + || strcmp (buf, expected[2]) == 0 + || strcmp (buf, expected[3]) == 0) + return 0; + else + { + printf ("snprintf (\"%s\", %a) returned \"%s\" not " + "\"%s\" or \"%s\" or \"%s\" or \"%s\" (%s)\n", + fmt, d, buf, expected[0], expected[1], expected[2], expected[3], + mode_name); + return 1; + } +} + static int do_test (void) { @@ -103,6 +196,37 @@ do_test (void) } #endif } + + for (size_t i = 0; i < sizeof (hex_tests) / sizeof (hex_tests[0]); i++) + { + result |= test_hex_in_one_mode (hex_tests[i].d, hex_tests[i].fmt, + hex_tests[i].rn, "default rounding mode"); +#ifdef FE_DOWNWARD + if (!fesetround (FE_DOWNWARD)) + { + result |= test_hex_in_one_mode (hex_tests[i].d, hex_tests[i].fmt, + hex_tests[i].rd, "FE_DOWNWARD"); + fesetround (save_round_mode); + } +#endif +#ifdef FE_TOWARDZERO + if (!fesetround (FE_TOWARDZERO)) + { + result |= test_hex_in_one_mode (hex_tests[i].d, hex_tests[i].fmt, + hex_tests[i].rz, "FE_TOWARDZERO"); + fesetround (save_round_mode); + } +#endif +#ifdef FE_UPWARD + if (!fesetround (FE_UPWARD)) + { + result |= test_hex_in_one_mode (hex_tests[i].d, hex_tests[i].fmt, + hex_tests[i].ru, "FE_UPWARD"); + fesetround (save_round_mode); + } +#endif + } + return result; } -- 2.7.4