| /* Print floating point number in hexadecimal notation according to ISO C99. | |
| Copyright (C) 1997-2012 Free Software Foundation, Inc. | |
| This file is part of the GNU C Library. | |
| Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997. | |
| 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 <config.h> | |
| #include <math.h> | |
| #include <stdlib.h> | |
| #include <stdio.h> | |
| #include <string.h> | |
| #include <stdbool.h> | |
| #define NDEBUG | |
| #include <assert.h> | |
| #include "quadmath-rounding-mode.h" | |
| #include "quadmath-printf.h" | |
| #include "_itoa.h" | |
| #include "_itowa.h" | |
| /* Macros for doing the actual output. */ | |
| #define outchar(ch) \ | |
| do \ | |
| { \ | |
| register const int outc = (ch); \ | |
| if (PUTC (outc, fp) == EOF) \ | |
| return -1; \ | |
| ++done; \ | |
| } while (0) | |
| #define PRINT(ptr, wptr, len) \ | |
| do \ | |
| { \ | |
| register size_t outlen = (len); \ | |
| if (wide) \ | |
| while (outlen-- > 0) \ | |
| outchar (*wptr++); \ | |
| else \ | |
| while (outlen-- > 0) \ | |
| outchar (*ptr++); \ | |
| } while (0) | |
| #define PADN(ch, len) \ | |
| do \ | |
| { \ | |
| if (PAD (fp, ch, len) != len) \ | |
| return -1; \ | |
| done += len; \ | |
| } \ | |
| while (0) | |
| int | |
| __quadmath_printf_fphex (struct __quadmath_printf_file *fp, | |
| const struct printf_info *info, | |
| const void *const *args) | |
| { | |
| /* The floating-point value to output. */ | |
| ieee854_float128 fpnum; | |
| /* Locale-dependent representation of decimal point. */ | |
| const char *decimal; | |
| wchar_t decimalwc; | |
| /* "NaN" or "Inf" for the special cases. */ | |
| const char *special = NULL; | |
| const wchar_t *wspecial = NULL; | |
| /* Buffer for the generated number string for the mantissa. The | |
| maximal size for the mantissa is 128 bits. */ | |
| char numbuf[32]; | |
| char *numstr; | |
| char *numend; | |
| wchar_t wnumbuf[32]; | |
| wchar_t *wnumstr; | |
| wchar_t *wnumend; | |
| int negative; | |
| /* The maximal exponent of two in decimal notation has 5 digits. */ | |
| char expbuf[5]; | |
| char *expstr; | |
| wchar_t wexpbuf[5]; | |
| wchar_t *wexpstr; | |
| int expnegative; | |
| int exponent; | |
| /* Non-zero is mantissa is zero. */ | |
| int zero_mantissa; | |
| /* The leading digit before the decimal point. */ | |
| char leading; | |
| /* Precision. */ | |
| int precision = info->prec; | |
| /* Width. */ | |
| int width = info->width; | |
| /* Number of characters written. */ | |
| int done = 0; | |
| /* Nonzero if this is output on a wide character stream. */ | |
| int wide = info->wide; | |
| bool do_round_away; | |
| /* Figure out the decimal point character. */ | |
| #ifdef USE_NL_LANGINFO | |
| if (info->extra == 0) | |
| decimal = nl_langinfo (DECIMAL_POINT); | |
| else | |
| { | |
| decimal = nl_langinfo (MON_DECIMAL_POINT); | |
| if (*decimal == '\0') | |
| decimal = nl_langinfo (DECIMAL_POINT); | |
| } | |
| /* The decimal point character must never be zero. */ | |
| assert (*decimal != '\0'); | |
| #elif defined USE_LOCALECONV | |
| const struct lconv *lc = localeconv (); | |
| if (info->extra == 0) | |
| decimal = lc->decimal_point; | |
| else | |
| { | |
| decimal = lc->mon_decimal_point; | |
| if (decimal == NULL || *decimal == '\0') | |
| decimal = lc->decimal_point; | |
| } | |
| if (decimal == NULL || *decimal == '\0') | |
| decimal = "."; | |
| #else | |
| decimal = "."; | |
| #endif | |
| #ifdef USE_NL_LANGINFO_WC | |
| if (info->extra == 0) | |
| decimalwc = nl_langinfo_wc (_NL_NUMERIC_DECIMAL_POINT_WC); | |
| else | |
| { | |
| decimalwc = nl_langinfo_wc (_NL_MONETARY_DECIMAL_POINT_WC); | |
| if (decimalwc == L_('\0')) | |
| decimalwc = nl_langinfo_wc (_NL_NUMERIC_DECIMAL_POINT_WC); | |
| } | |
| /* The decimal point character must never be zero. */ | |
| assert (decimalwc != L_('\0')); | |
| #else | |
| decimalwc = L_('.'); | |
| #endif | |
| /* Fetch the argument value. */ | |
| { | |
| memcpy (&fpnum.value, *(const void *const *) args[0], | |
| sizeof (fpnum.value)); | |
| /* Check for special values: not a number or infinity. */ | |
| if (isnanq (fpnum.value)) | |
| { | |
| negative = fpnum.ieee.negative != 0; | |
| if (isupper (info->spec)) | |
| { | |
| special = "NAN"; | |
| wspecial = L_("NAN"); | |
| } | |
| else | |
| { | |
| special = "nan"; | |
| wspecial = L_("nan"); | |
| } | |
| } | |
| else | |
| { | |
| if (isinfq (fpnum.value)) | |
| { | |
| if (isupper (info->spec)) | |
| { | |
| special = "INF"; | |
| wspecial = L_("INF"); | |
| } | |
| else | |
| { | |
| special = "inf"; | |
| wspecial = L_("inf"); | |
| } | |
| } | |
| negative = signbitq (fpnum.value); | |
| } | |
| } | |
| if (special) | |
| { | |
| int width = info->width; | |
| if (negative || info->showsign || info->space) | |
| --width; | |
| width -= 3; | |
| if (!info->left && width > 0) | |
| PADN (' ', width); | |
| if (negative) | |
| outchar ('-'); | |
| else if (info->showsign) | |
| outchar ('+'); | |
| else if (info->space) | |
| outchar (' '); | |
| PRINT (special, wspecial, 3); | |
| if (info->left && width > 0) | |
| PADN (' ', width); | |
| return done; | |
| } | |
| { | |
| /* We have 112 bits of mantissa plus one implicit digit. Since | |
| 112 bits are representable without rest using hexadecimal | |
| digits we use only the implicit digits for the number before | |
| the decimal point. */ | |
| uint64_t num0, num1; | |
| assert (sizeof (long double) == 16); | |
| num0 = (((unsigned long long int) fpnum.ieee.mantissa0) << 32 | |
| | fpnum.ieee.mantissa1); | |
| num1 = (((unsigned long long int) fpnum.ieee.mantissa2) << 32 | |
| | fpnum.ieee.mantissa3); | |
| zero_mantissa = (num0|num1) == 0; | |
| if (sizeof (unsigned long int) > 6) | |
| { | |
| numstr = _itoa_word (num1, numbuf + sizeof numbuf, 16, | |
| info->spec == 'A'); | |
| wnumstr = _itowa_word (num1, | |
| wnumbuf + sizeof (wnumbuf) / sizeof (wchar_t), | |
| 16, info->spec == 'A'); | |
| } | |
| else | |
| { | |
| numstr = _itoa (num1, numbuf + sizeof numbuf, 16, | |
| info->spec == 'A'); | |
| wnumstr = _itowa (num1, | |
| wnumbuf + sizeof (wnumbuf) / sizeof (wchar_t), | |
| 16, info->spec == 'A'); | |
| } | |
| while (numstr > numbuf + (sizeof numbuf - 64 / 4)) | |
| { | |
| *--numstr = '0'; | |
| *--wnumstr = L_('0'); | |
| } | |
| if (sizeof (unsigned long int) > 6) | |
| { | |
| numstr = _itoa_word (num0, numstr, 16, info->spec == 'A'); | |
| wnumstr = _itowa_word (num0, wnumstr, 16, info->spec == 'A'); | |
| } | |
| else | |
| { | |
| numstr = _itoa (num0, numstr, 16, info->spec == 'A'); | |
| wnumstr = _itowa (num0, wnumstr, 16, info->spec == 'A'); | |
| } | |
| /* Fill with zeroes. */ | |
| while (numstr > numbuf + (sizeof numbuf - 112 / 4)) | |
| { | |
| *--wnumstr = L_('0'); | |
| *--numstr = '0'; | |
| } | |
| leading = fpnum.ieee.exponent == 0 ? '0' : '1'; | |
| exponent = fpnum.ieee.exponent; | |
| if (exponent == 0) | |
| { | |
| if (zero_mantissa) | |
| expnegative = 0; | |
| else | |
| { | |
| /* This is a denormalized number. */ | |
| expnegative = 1; | |
| exponent = IEEE854_FLOAT128_BIAS - 1; | |
| } | |
| } | |
| else if (exponent >= IEEE854_FLOAT128_BIAS) | |
| { | |
| expnegative = 0; | |
| exponent -= IEEE854_FLOAT128_BIAS; | |
| } | |
| else | |
| { | |
| expnegative = 1; | |
| exponent = -(exponent - IEEE854_FLOAT128_BIAS); | |
| } | |
| } | |
| /* Look for trailing zeroes. */ | |
| if (! zero_mantissa) | |
| { | |
| wnumend = &wnumbuf[sizeof wnumbuf / sizeof wnumbuf[0]]; | |
| numend = &numbuf[sizeof numbuf / sizeof numbuf[0]]; | |
| while (wnumend[-1] == L_('0')) | |
| { | |
| --wnumend; | |
| --numend; | |
| } | |
| 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); | |
| #ifdef HAVE_FENV_H | |
| int rounding_mode = get_rounding_mode (); | |
| do_round_away = round_away (negative, last_digit_value & 1, | |
| next_digit_value >= 8, more_bits, | |
| rounding_mode); | |
| #endif | |
| } | |
| if (precision == -1) | |
| precision = numend - numstr; | |
| else if (do_round_away) | |
| { | |
| /* Round up. */ | |
| int cnt = precision; | |
| while (--cnt >= 0) | |
| { | |
| char ch = numstr[cnt]; | |
| /* We assume that the digits and the letters are ordered | |
| like in ASCII. This is true for the rest of GNU, too. */ | |
| if (ch == '9') | |
| { | |
| wnumstr[cnt] = (wchar_t) info->spec; | |
| numstr[cnt] = info->spec; /* This is tricky, | |
| think about it! */ | |
| break; | |
| } | |
| else if (tolower (ch) < 'f') | |
| { | |
| ++numstr[cnt]; | |
| ++wnumstr[cnt]; | |
| break; | |
| } | |
| else | |
| { | |
| numstr[cnt] = '0'; | |
| wnumstr[cnt] = L_('0'); | |
| } | |
| } | |
| if (cnt < 0) | |
| { | |
| /* The mantissa so far was fff...f Now increment the | |
| leading digit. Here it is again possible that we | |
| get an overflow. */ | |
| if (leading == '9') | |
| leading = info->spec; | |
| else if (tolower (leading) < 'f') | |
| ++leading; | |
| else | |
| { | |
| leading = '1'; | |
| if (expnegative) | |
| { | |
| exponent -= 4; | |
| if (exponent <= 0) | |
| { | |
| exponent = -exponent; | |
| expnegative = 0; | |
| } | |
| } | |
| else | |
| exponent += 4; | |
| } | |
| } | |
| } | |
| } | |
| else | |
| { | |
| if (precision == -1) | |
| precision = 0; | |
| numend = numstr; | |
| wnumend = wnumstr; | |
| } | |
| /* Now we can compute the exponent string. */ | |
| expstr = _itoa_word (exponent, expbuf + sizeof expbuf, 10, 0); | |
| wexpstr = _itowa_word (exponent, | |
| wexpbuf + sizeof wexpbuf / sizeof (wchar_t), 10, 0); | |
| /* Now we have all information to compute the size. */ | |
| width -= ((negative || info->showsign || info->space) | |
| /* Sign. */ | |
| + 2 + 1 + 0 + precision + 1 + 1 | |
| /* 0x h . hhh P ExpoSign. */ | |
| + ((expbuf + sizeof expbuf) - expstr)); | |
| /* Exponent. */ | |
| /* Count the decimal point. | |
| A special case when the mantissa or the precision is zero and the `#' | |
| is not given. In this case we must not print the decimal point. */ | |
| if (precision > 0 || info->alt) | |
| width -= wide ? 1 : strlen (decimal); | |
| if (!info->left && info->pad != '0' && width > 0) | |
| PADN (' ', width); | |
| if (negative) | |
| outchar ('-'); | |
| else if (info->showsign) | |
| outchar ('+'); | |
| else if (info->space) | |
| outchar (' '); | |
| outchar ('0'); | |
| if ('X' - 'A' == 'x' - 'a') | |
| outchar (info->spec + ('x' - 'a')); | |
| else | |
| outchar (info->spec == 'A' ? 'X' : 'x'); | |
| if (!info->left && info->pad == '0' && width > 0) | |
| PADN ('0', width); | |
| outchar (leading); | |
| if (precision > 0 || info->alt) | |
| { | |
| const wchar_t *wtmp = &decimalwc; | |
| PRINT (decimal, wtmp, wide ? 1 : strlen (decimal)); | |
| } | |
| if (precision > 0) | |
| { | |
| ssize_t tofill = precision - (numend - numstr); | |
| PRINT (numstr, wnumstr, MIN (numend - numstr, precision)); | |
| if (tofill > 0) | |
| PADN ('0', tofill); | |
| } | |
| if ('P' - 'A' == 'p' - 'a') | |
| outchar (info->spec + ('p' - 'a')); | |
| else | |
| outchar (info->spec == 'A' ? 'P' : 'p'); | |
| outchar (expnegative ? '-' : '+'); | |
| PRINT (expstr, wexpstr, (expbuf + sizeof expbuf) - expstr); | |
| if (info->left && info->pad != '0' && width > 0) | |
| PADN (info->pad, width); | |
| return done; | |
| } |