Back to index

glibc  2.9
printf_fphex.c
Go to the documentation of this file.
00001 /* Print floating point number in hexadecimal notation according to ISO C99.
00002    Copyright (C) 1997-2002,2004,2006 Free Software Foundation, Inc.
00003    This file is part of the GNU C Library.
00004    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997.
00005 
00006    The GNU C Library is free software; you can redistribute it and/or
00007    modify it under the terms of the GNU Lesser General Public
00008    License as published by the Free Software Foundation; either
00009    version 2.1 of the License, or (at your option) any later version.
00010 
00011    The GNU C Library is distributed in the hope that it will be useful,
00012    but WITHOUT ANY WARRANTY; without even the implied warranty of
00013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014    Lesser General Public License for more details.
00015 
00016    You should have received a copy of the GNU Lesser General Public
00017    License along with the GNU C Library; if not, write to the Free
00018    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
00019    02111-1307 USA.  */
00020 
00021 #include <ctype.h>
00022 #include <ieee754.h>
00023 #include <math.h>
00024 #include <printf.h>
00025 #include <stdlib.h>
00026 #include <stdio.h>
00027 #include <string.h>
00028 #include <wchar.h>
00029 #include "_itoa.h"
00030 #include "_itowa.h"
00031 #include <locale/localeinfo.h>
00032 
00033 /* #define NDEBUG 1*/              /* Undefine this for debugging assertions.  */
00034 #include <assert.h>
00035 
00036 /* This defines make it possible to use the same code for GNU C library and
00037    the GNU I/O library.      */
00038 #ifdef USE_IN_LIBIO
00039 # include <libioP.h>
00040 # define PUT(f, s, n) _IO_sputn (f, s, n)
00041 # define PAD(f, c, n) (wide ? _IO_wpadn (f, c, n) : INTUSE(_IO_padn) (f, c, n))
00042 /* We use this file GNU C library and GNU I/O library.  So make
00043    names equal.       */
00044 # undef putc
00045 # define putc(c, f) (wide \
00046                    ? (int)_IO_putwc_unlocked (c, f) : _IO_putc_unlocked (c, f))
00047 # define size_t     _IO_size_t
00048 # define FILE      _IO_FILE
00049 #else  /* ! USE_IN_LIBIO */
00050 # define PUT(f, s, n) fwrite (s, 1, n, f)
00051 # define PAD(f, c, n) __printf_pad (f, c, n)
00052 ssize_t __printf_pad (FILE *, char pad, int n) __THROW; /* In vfprintf.c.  */
00053 #endif /* USE_IN_LIBIO */
00054 
00055 /* Macros for doing the actual output.  */
00056 
00057 #define outchar(ch)                                                  \
00058   do                                                                 \
00059     {                                                                \
00060       register const int outc = (ch);                                       \
00061       if (putc (outc, fp) == EOF)                                    \
00062        return -1;                                                    \
00063       ++done;                                                        \
00064     } while (0)
00065 
00066 #define PRINT(ptr, wptr, len)                                               \
00067   do                                                                 \
00068     {                                                                \
00069       register size_t outlen = (len);                                       \
00070       if (wide)                                                             \
00071        while (outlen-- > 0)                                          \
00072          outchar (*wptr++);                                          \
00073       else                                                           \
00074        while (outlen-- > 0)                                          \
00075          outchar (*ptr++);                                           \
00076     } while (0)
00077 
00078 #define PADN(ch, len)                                                       \
00079   do                                                                 \
00080     {                                                                \
00081       if (PAD (fp, ch, len) != len)                                         \
00082        return -1;                                                    \
00083       done += len;                                                   \
00084     }                                                                \
00085   while (0)
00086 
00087 #ifndef MIN
00088 # define MIN(a,b) ((a)<(b)?(a):(b))
00089 #endif
00090 
00091 
00092 int
00093 __printf_fphex (FILE *fp,
00094               const struct printf_info *info,
00095               const void *const *args)
00096 {
00097   /* The floating-point value to output.  */
00098   union
00099     {
00100       union ieee754_double dbl;
00101       union ieee854_long_double ldbl;
00102     }
00103   fpnum;
00104 
00105   /* Locale-dependent representation of decimal point.  */
00106   const char *decimal;
00107   wchar_t decimalwc;
00108 
00109   /* "NaN" or "Inf" for the special cases.  */
00110   const char *special = NULL;
00111   const wchar_t *wspecial = NULL;
00112 
00113   /* Buffer for the generated number string for the mantissa.  The
00114      maximal size for the mantissa is 128 bits.  */
00115   char numbuf[32];
00116   char *numstr;
00117   char *numend;
00118   wchar_t wnumbuf[32];
00119   wchar_t *wnumstr;
00120   wchar_t *wnumend;
00121   int negative;
00122 
00123   /* The maximal exponent of two in decimal notation has 5 digits.  */
00124   char expbuf[5];
00125   char *expstr;
00126   wchar_t wexpbuf[5];
00127   wchar_t *wexpstr;
00128   int expnegative;
00129   int exponent;
00130 
00131   /* Non-zero is mantissa is zero.  */
00132   int zero_mantissa;
00133 
00134   /* The leading digit before the decimal point.  */
00135   char leading;
00136 
00137   /* Precision.  */
00138   int precision = info->prec;
00139 
00140   /* Width.  */
00141   int width = info->width;
00142 
00143   /* Number of characters written.  */
00144   int done = 0;
00145 
00146   /* Nonzero if this is output on a wide character stream.  */
00147   int wide = info->wide;
00148 
00149 
00150   /* Figure out the decimal point character.  */
00151   if (info->extra == 0)
00152     {
00153       decimal = _NL_CURRENT (LC_NUMERIC, DECIMAL_POINT);
00154       decimalwc = _NL_CURRENT_WORD (LC_NUMERIC, _NL_NUMERIC_DECIMAL_POINT_WC);
00155     }
00156   else
00157     {
00158       decimal = _NL_CURRENT (LC_MONETARY, MON_DECIMAL_POINT);
00159       decimalwc = _NL_CURRENT_WORD (LC_MONETARY,
00160                                 _NL_MONETARY_DECIMAL_POINT_WC);
00161     }
00162   /* The decimal point character must never be zero.  */
00163   assert (*decimal != '\0' && decimalwc != L'\0');
00164 
00165 
00166   /* Fetch the argument value.     */
00167 #ifndef __NO_LONG_DOUBLE_MATH
00168   if (info->is_long_double && sizeof (long double) > sizeof (double))
00169     {
00170       fpnum.ldbl.d = *(const long double *) args[0];
00171 
00172       /* Check for special values: not a number or infinity.  */
00173       if (__isnanl (fpnum.ldbl.d))
00174        {
00175          if (isupper (info->spec))
00176            {
00177              special = "NAN";
00178              wspecial = L"NAN";
00179            }
00180          else
00181            {
00182              special = "nan";
00183              wspecial = L"nan";
00184            }
00185          negative = 0;
00186        }
00187       else
00188        {
00189          if (__isinfl (fpnum.ldbl.d))
00190            {
00191              if (isupper (info->spec))
00192               {
00193                 special = "INF";
00194                 wspecial = L"INF";
00195               }
00196              else
00197               {
00198                 special = "inf";
00199                 wspecial = L"inf";
00200               }
00201            }
00202 
00203          negative = signbit (fpnum.ldbl.d);
00204        }
00205     }
00206   else
00207 #endif /* no long double */
00208     {
00209       fpnum.dbl.d = *(const double *) args[0];
00210 
00211       /* Check for special values: not a number or infinity.  */
00212       if (__isnan (fpnum.dbl.d))
00213        {
00214          if (isupper (info->spec))
00215            {
00216              special = "NAN";
00217              wspecial = L"NAN";
00218            }
00219          else
00220            {
00221              special = "nan";
00222              wspecial = L"nan";
00223            }
00224          negative = 0;
00225        }
00226       else
00227        {
00228          if (__isinf (fpnum.dbl.d))
00229            {
00230              if (isupper (info->spec))
00231               {
00232                 special = "INF";
00233                 wspecial = L"INF";
00234               }
00235              else
00236               {
00237                 special = "inf";
00238                 wspecial = L"inf";
00239               }
00240            }
00241 
00242          negative = signbit (fpnum.dbl.d);
00243        }
00244     }
00245 
00246   if (special)
00247     {
00248       int width = info->width;
00249 
00250       if (negative || info->showsign || info->space)
00251        --width;
00252       width -= 3;
00253 
00254       if (!info->left && width > 0)
00255        PADN (' ', width);
00256 
00257       if (negative)
00258        outchar ('-');
00259       else if (info->showsign)
00260        outchar ('+');
00261       else if (info->space)
00262        outchar (' ');
00263 
00264       PRINT (special, wspecial, 3);
00265 
00266       if (info->left && width > 0)
00267        PADN (' ', width);
00268 
00269       return done;
00270     }
00271 
00272   if (info->is_long_double == 0 || sizeof (double) == sizeof (long double))
00273     {
00274       /* We have 52 bits of mantissa plus one implicit digit.  Since
00275         52 bits are representable without rest using hexadecimal
00276         digits we use only the implicit digits for the number before
00277         the decimal point.  */
00278       unsigned long long int num;
00279 
00280       num = (((unsigned long long int) fpnum.dbl.ieee.mantissa0) << 32
00281             | fpnum.dbl.ieee.mantissa1);
00282 
00283       zero_mantissa = num == 0;
00284 
00285       if (sizeof (unsigned long int) > 6)
00286        {
00287          wnumstr = _itowa_word (num, wnumbuf + (sizeof wnumbuf) / sizeof (wchar_t), 16,
00288                              info->spec == 'A');
00289          numstr = _itoa_word (num, numbuf + sizeof numbuf, 16,
00290                             info->spec == 'A');
00291        }
00292       else
00293        {
00294          wnumstr = _itowa (num, wnumbuf + sizeof wnumbuf / sizeof (wchar_t), 16,
00295                          info->spec == 'A');
00296          numstr = _itoa (num, numbuf + sizeof numbuf, 16,
00297                        info->spec == 'A');
00298        }
00299 
00300       /* Fill with zeroes.  */
00301       while (wnumstr > wnumbuf + (sizeof wnumbuf - 52) / sizeof (wchar_t))
00302        {
00303          *--wnumstr = L'0';
00304          *--numstr = '0';
00305        }
00306 
00307       leading = fpnum.dbl.ieee.exponent == 0 ? '0' : '1';
00308 
00309       exponent = fpnum.dbl.ieee.exponent;
00310 
00311       if (exponent == 0)
00312        {
00313          if (zero_mantissa)
00314            expnegative = 0;
00315          else
00316            {
00317              /* This is a denormalized number.  */
00318              expnegative = 1;
00319              exponent = IEEE754_DOUBLE_BIAS - 1;
00320            }
00321        }
00322       else if (exponent >= IEEE754_DOUBLE_BIAS)
00323        {
00324          expnegative = 0;
00325          exponent -= IEEE754_DOUBLE_BIAS;
00326        }
00327       else
00328        {
00329          expnegative = 1;
00330          exponent = -(exponent - IEEE754_DOUBLE_BIAS);
00331        }
00332     }
00333 #ifdef PRINT_FPHEX_LONG_DOUBLE
00334   else
00335     PRINT_FPHEX_LONG_DOUBLE;
00336 #endif
00337 
00338   /* Look for trailing zeroes.  */
00339   if (! zero_mantissa)
00340     {
00341       wnumend = &wnumbuf[sizeof wnumbuf / sizeof wnumbuf[0]];
00342       numend = &numbuf[sizeof numbuf / sizeof numbuf[0]];
00343       while (wnumend[-1] == L'0')
00344        {
00345          --wnumend;
00346          --numend;
00347        }
00348 
00349       if (precision == -1)
00350        precision = numend - numstr;
00351       else if (precision < numend - numstr
00352               && (numstr[precision] > '8'
00353                  || (('A' < '0' || 'a' < '0')
00354                      && numstr[precision] < '0')
00355                  || (numstr[precision] == '8'
00356                      && (precision + 1 < numend - numstr
00357                         /* Round to even.  */
00358                         || (precision > 0
00359                             && ((numstr[precision - 1] & 1)
00360                                ^ (isdigit (numstr[precision - 1]) == 0)))
00361                         || (precision == 0
00362                             && ((leading & 1)
00363                                ^ (isdigit (leading) == 0)))))))
00364        {
00365          /* Round up.  */
00366          int cnt = precision;
00367          while (--cnt >= 0)
00368            {
00369              char ch = numstr[cnt];
00370              /* We assume that the digits and the letters are ordered
00371                like in ASCII.  This is true for the rest of GNU, too.  */
00372              if (ch == '9')
00373               {
00374                 wnumstr[cnt] = (wchar_t) info->spec;
00375                 numstr[cnt] = info->spec; /* This is tricky,
00376                                              think about it!  */
00377                 break;
00378               }
00379              else if (tolower (ch) < 'f')
00380               {
00381                 ++numstr[cnt];
00382                 ++wnumstr[cnt];
00383                 break;
00384               }
00385              else
00386               {
00387                 numstr[cnt] = '0';
00388                 wnumstr[cnt] = L'0';
00389               }
00390            }
00391          if (cnt < 0)
00392            {
00393              /* The mantissa so far was fff...f  Now increment the
00394                leading digit.  Here it is again possible that we
00395                get an overflow.  */
00396              if (leading == '9')
00397               leading = info->spec;
00398              else if (tolower (leading) < 'f')
00399               ++leading;
00400              else
00401               {
00402                 leading = '1';
00403                 if (expnegative)
00404                   {
00405                     exponent -= 4;
00406                     if (exponent <= 0)
00407                      {
00408                        exponent = -exponent;
00409                        expnegative = 0;
00410                      }
00411                   }
00412                 else
00413                   exponent += 4;
00414               }
00415            }
00416        }
00417     }
00418   else
00419     {
00420       if (precision == -1)
00421        precision = 0;
00422       numend = numstr;
00423       wnumend = wnumstr;
00424     }
00425 
00426   /* Now we can compute the exponent string.  */
00427   expstr = _itoa_word (exponent, expbuf + sizeof expbuf, 10, 0);
00428   wexpstr = _itowa_word (exponent,
00429                       wexpbuf + sizeof wexpbuf / sizeof (wchar_t), 10, 0);
00430 
00431   /* Now we have all information to compute the size.  */
00432   width -= ((negative || info->showsign || info->space)
00433            /* Sign.  */
00434            + 2    + 1 + 0 + precision + 1 + 1
00435            /* 0x    h   .   hhh         P   ExpoSign.  */
00436            + ((expbuf + sizeof expbuf) - expstr));
00437            /* Exponent.  */
00438 
00439   /* Count the decimal point.
00440      A special case when the mantissa or the precision is zero and the `#'
00441      is not given.  In this case we must not print the decimal point.  */
00442   if (precision > 0 || info->alt)
00443     width -= wide ? 1 : strlen (decimal);
00444 
00445   if (!info->left && info->pad != '0' && width > 0)
00446     PADN (' ', width);
00447 
00448   if (negative)
00449     outchar ('-');
00450   else if (info->showsign)
00451     outchar ('+');
00452   else if (info->space)
00453     outchar (' ');
00454 
00455   outchar ('0');
00456   if ('X' - 'A' == 'x' - 'a')
00457     outchar (info->spec + ('x' - 'a'));
00458   else
00459     outchar (info->spec == 'A' ? 'X' : 'x');
00460 
00461   if (!info->left && info->pad == '0' && width > 0)
00462     PADN ('0', width);
00463 
00464   outchar (leading);
00465 
00466   if (precision > 0 || info->alt)
00467     {
00468       const wchar_t *wtmp = &decimalwc;
00469       PRINT (decimal, wtmp, wide ? 1 : strlen (decimal));
00470     }
00471 
00472   if (precision > 0)
00473     {
00474       ssize_t tofill = precision - (numend - numstr);
00475       PRINT (numstr, wnumstr, MIN (numend - numstr, precision));
00476       if (tofill > 0)
00477        PADN ('0', tofill);
00478     }
00479 
00480   if ('P' - 'A' == 'p' - 'a')
00481     outchar (info->spec + ('p' - 'a'));
00482   else
00483     outchar (info->spec == 'A' ? 'P' : 'p');
00484 
00485   outchar (expnegative ? '-' : '+');
00486 
00487   PRINT (expstr, wexpstr, (expbuf + sizeof expbuf) - expstr);
00488 
00489   if (info->left && info->pad != '0' && width > 0)
00490     PADN (info->pad, width);
00491 
00492   return done;
00493 }