diff options
author | Joseph Myers <joseph@codesourcery.com> | 2012-09-24 15:38:21 +0000 |
---|---|---|
committer | Joseph Myers <joseph@codesourcery.com> | 2012-09-24 15:38:21 +0000 |
commit | a9f8e53a5b14ba481999ded036b025554ea06362 (patch) | |
tree | a59b5925b071a62962c3d399bc8758a690f518bd /stdio-common | |
parent | eaf6f205fff7ca8a628869e3bc56800020a70f24 (diff) | |
download | glibc-a9f8e53a5b14ba481999ded036b025554ea06362.tar.gz |
Make printf respect the rounding mode for hex output (bug 5044).
Diffstat (limited to 'stdio-common')
-rw-r--r-- | stdio-common/printf_fphex.c | 40 | ||||
-rw-r--r-- | stdio-common/tst-printf-round.c | 124 |
2 files changed, 151 insertions, 13 deletions
diff --git a/stdio-common/printf_fphex.c b/stdio-common/printf_fphex.c index 13491455d1..2224196b24 100644 --- a/stdio-common/printf_fphex.c +++ b/stdio-common/printf_fphex.c @@ -28,6 +28,8 @@ #include <_itoa.h> #include <_itowa.h> #include <locale/localeinfo.h> +#include <stdbool.h> +#include <rounding-mode.h> /* #define NDEBUG 1*/ /* Undefine this for debugging assertions. */ #include <assert.h> @@ -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 2bd46079ac..7cc19c5645 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; } |