(root)/
glibc-2.38/
stdio-common/
printf_fphex.c
       1  /* Print floating point number in hexadecimal notation according to ISO C99.
       2     Copyright (C) 1997-2023 Free Software Foundation, Inc.
       3     This file is part of the GNU C Library.
       4  
       5     The GNU C Library is free software; you can redistribute it and/or
       6     modify it under the terms of the GNU Lesser General Public
       7     License as published by the Free Software Foundation; either
       8     version 2.1 of the License, or (at your option) any later version.
       9  
      10     The GNU C Library is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13     Lesser General Public License for more details.
      14  
      15     You should have received a copy of the GNU Lesser General Public
      16     License along with the GNU C Library; if not, see
      17     <https://www.gnu.org/licenses/>.  */
      18  
      19  #include <array_length.h>
      20  #include <assert.h>
      21  #include <ctype.h>
      22  #include <ieee754.h>
      23  #include <math.h>
      24  #include <printf.h>
      25  #include <libioP.h>
      26  #include <stdlib.h>
      27  #include <stdio.h>
      28  #include <string.h>
      29  #include <wchar.h>
      30  #include <_itoa.h>
      31  #include <_itowa.h>
      32  #include <locale/localeinfo.h>
      33  #include <stdbool.h>
      34  #include <rounding-mode.h>
      35  #include <sys/param.h>
      36  #include <printf_buffer.h>
      37  #include <errno.h>
      38  
      39  #if __HAVE_DISTINCT_FLOAT128
      40  # include "ieee754_float128.h"
      41  # include <ldbl-128/printf_fphex_macros.h>
      42  # define PRINT_FPHEX_FLOAT128 \
      43     PRINT_FPHEX (_Float128, fpnum.flt128, ieee854_float128, \
      44  		IEEE854_FLOAT128_BIAS)
      45  #endif
      46  
      47  static void
      48  __printf_fphex_buffer (struct __printf_buffer *buf,
      49  		       const char *decimal,
      50  		       const struct printf_info *info,
      51  		       const void *const *args)
      52  {
      53    /* The floating-point value to output.  */
      54    union
      55      {
      56        union ieee754_double dbl;
      57        long double ldbl;
      58  #if __HAVE_DISTINCT_FLOAT128
      59        _Float128 flt128;
      60  #endif
      61      }
      62    fpnum;
      63  
      64    /* This function always uses LC_NUMERIC.  */
      65    assert (info->extra == 0);
      66  
      67    /* "NaN" or "Inf" for the special cases.  */
      68    const char *special = NULL;
      69  
      70    /* Buffer for the generated number string for the mantissa.  The
      71       maximal size for the mantissa is 128 bits.  */
      72    char numbuf[32];
      73    char *numstr;
      74    char *numend;
      75    int negative;
      76  
      77    /* The maximal exponent of two in decimal notation has 5 digits.  */
      78    char expbuf[5];
      79    char *expstr;
      80    int expnegative;
      81    int exponent;
      82  
      83    /* Non-zero is mantissa is zero.  */
      84    int zero_mantissa;
      85  
      86    /* The leading digit before the decimal point.  */
      87    char leading;
      88  
      89    /* Precision.  */
      90    int precision = info->prec;
      91  
      92    /* Width.  */
      93    int width = info->width;
      94  
      95  #define PRINTF_FPHEX_FETCH(FLOAT, VAR)					\
      96    {									\
      97      (VAR) = *(const FLOAT *) args[0];					\
      98  									\
      99      /* Check for special values: not a number or infinity.  */		\
     100      if (isnan (VAR))							\
     101        {									\
     102  	if (isupper (info->spec))					\
     103  	  special = "NAN";						\
     104  	else								\
     105  	  special = "nan";						\
     106        }									\
     107      else								\
     108        {									\
     109  	if (isinf (VAR))						\
     110  	  {								\
     111  	    if (isupper (info->spec))					\
     112  	      special = "INF";						\
     113  	    else							\
     114  	      special = "inf";						\
     115  	  }								\
     116        }									\
     117      negative = signbit (VAR);						\
     118    }
     119  
     120    /* Fetch the argument value.	*/
     121  #if __HAVE_DISTINCT_FLOAT128
     122    if (info->is_binary128)
     123      PRINTF_FPHEX_FETCH (_Float128, fpnum.flt128)
     124    else
     125  #endif
     126  #ifndef __NO_LONG_DOUBLE_MATH
     127    if (info->is_long_double && sizeof (long double) > sizeof (double))
     128      PRINTF_FPHEX_FETCH (long double, fpnum.ldbl)
     129    else
     130  #endif
     131      PRINTF_FPHEX_FETCH (double, fpnum.dbl.d)
     132  
     133  #undef PRINTF_FPHEX_FETCH
     134  
     135    if (special)
     136      {
     137        int width = info->width;
     138  
     139        if (negative || info->showsign || info->space)
     140  	--width;
     141        width -= 3;
     142  
     143        if (!info->left)
     144  	__printf_buffer_pad (buf, ' ', width);
     145  
     146        if (negative)
     147  	__printf_buffer_putc (buf, '-');
     148        else if (info->showsign)
     149  	__printf_buffer_putc (buf, '+');
     150        else if (info->space)
     151  	__printf_buffer_putc (buf, ' ');
     152  
     153        __printf_buffer_puts (buf, special);
     154  
     155        if (info->left)
     156  	__printf_buffer_pad (buf, ' ', width);
     157  
     158        return;
     159      }
     160  
     161  #if __HAVE_DISTINCT_FLOAT128
     162    if (info->is_binary128)
     163      PRINT_FPHEX_FLOAT128;
     164    else
     165  #endif
     166    if (info->is_long_double == 0 || sizeof (double) == sizeof (long double))
     167      {
     168        /* We have 52 bits of mantissa plus one implicit digit.  Since
     169  	 52 bits are representable without rest using hexadecimal
     170  	 digits we use only the implicit digits for the number before
     171  	 the decimal point.  */
     172        unsigned long long int num;
     173  
     174        num = (((unsigned long long int) fpnum.dbl.ieee.mantissa0) << 32
     175  	     | fpnum.dbl.ieee.mantissa1);
     176  
     177        zero_mantissa = num == 0;
     178  
     179        if (sizeof (unsigned long int) > 6)
     180  	  numstr = _itoa_word (num, numbuf + sizeof numbuf, 16,
     181  			       info->spec == 'A');
     182        else
     183  	  numstr = _itoa (num, numbuf + sizeof numbuf, 16,
     184  			  info->spec == 'A');
     185  
     186        /* Fill with zeroes.  */
     187        while (numstr > numbuf + (sizeof numbuf - 13))
     188  	*--numstr = '0';
     189  
     190        leading = fpnum.dbl.ieee.exponent == 0 ? '0' : '1';
     191  
     192        exponent = fpnum.dbl.ieee.exponent;
     193  
     194        if (exponent == 0)
     195  	{
     196  	  if (zero_mantissa)
     197  	    expnegative = 0;
     198  	  else
     199  	    {
     200  	      /* This is a denormalized number.  */
     201  	      expnegative = 1;
     202  	      exponent = IEEE754_DOUBLE_BIAS - 1;
     203  	    }
     204  	}
     205        else if (exponent >= IEEE754_DOUBLE_BIAS)
     206  	{
     207  	  expnegative = 0;
     208  	  exponent -= IEEE754_DOUBLE_BIAS;
     209  	}
     210        else
     211  	{
     212  	  expnegative = 1;
     213  	  exponent = -(exponent - IEEE754_DOUBLE_BIAS);
     214  	}
     215      }
     216  #ifdef PRINT_FPHEX_LONG_DOUBLE
     217    else
     218      PRINT_FPHEX_LONG_DOUBLE;
     219  #endif
     220  
     221    /* Look for trailing zeroes.  */
     222    if (! zero_mantissa)
     223      {
     224        numend = array_end (numbuf);
     225        while (numend[-1] == '0')
     226  	  --numend;
     227  
     228        bool do_round_away = false;
     229  
     230        if (precision != -1 && precision < numend - numstr)
     231  	{
     232  	  char last_digit = precision > 0 ? numstr[precision - 1] : leading;
     233  	  char next_digit = numstr[precision];
     234  	  int last_digit_value = (last_digit >= 'A' && last_digit <= 'F'
     235  				  ? last_digit - 'A' + 10
     236  				  : (last_digit >= 'a' && last_digit <= 'f'
     237  				     ? last_digit - 'a' + 10
     238  				     : last_digit - '0'));
     239  	  int next_digit_value = (next_digit >= 'A' && next_digit <= 'F'
     240  				  ? next_digit - 'A' + 10
     241  				  : (next_digit >= 'a' && next_digit <= 'f'
     242  				     ? next_digit - 'a' + 10
     243  				     : next_digit - '0'));
     244  	  bool more_bits = ((next_digit_value & 7) != 0
     245  			    || precision + 1 < numend - numstr);
     246  	  int rounding_mode = get_rounding_mode ();
     247  	  do_round_away = round_away (negative, last_digit_value & 1,
     248  				      next_digit_value >= 8, more_bits,
     249  				      rounding_mode);
     250  	}
     251  
     252        if (precision == -1)
     253  	precision = numend - numstr;
     254        else if (do_round_away)
     255  	{
     256  	  /* Round up.  */
     257  	  int cnt = precision;
     258  	  while (--cnt >= 0)
     259  	    {
     260  	      char ch = numstr[cnt];
     261  	      /* We assume that the digits and the letters are ordered
     262  		 like in ASCII.  This is true for the rest of GNU, too.  */
     263  	      if (ch == '9')
     264  		{
     265  		  numstr[cnt] = info->spec;	/* This is tricky,
     266  						   think about it!  */
     267  		  break;
     268  		}
     269  	      else if (tolower (ch) < 'f')
     270  		{
     271  		  ++numstr[cnt];
     272  		  break;
     273  		}
     274  	      else
     275  		numstr[cnt] = '0';
     276  	    }
     277  	  if (cnt < 0)
     278  	    {
     279  	      /* The mantissa so far was fff...f  Now increment the
     280  		 leading digit.  Here it is again possible that we
     281  		 get an overflow.  */
     282  	      if (leading == '9')
     283  		leading = info->spec;
     284  	      else if (tolower (leading) < 'f')
     285  		++leading;
     286  	      else
     287  		{
     288  		  leading = '1';
     289  		  if (expnegative)
     290  		    {
     291  		      exponent -= 4;
     292  		      if (exponent <= 0)
     293  			{
     294  			  exponent = -exponent;
     295  			  expnegative = 0;
     296  			}
     297  		    }
     298  		  else
     299  		    exponent += 4;
     300  		}
     301  	    }
     302  	}
     303      }
     304    else
     305      {
     306        if (precision == -1)
     307  	precision = 0;
     308        numend = numstr;
     309      }
     310  
     311    /* Now we can compute the exponent string.  */
     312    expstr = _itoa_word (exponent, expbuf + sizeof expbuf, 10, 0);
     313  
     314    /* Now we have all information to compute the size.  */
     315    width -= ((negative || info->showsign || info->space)
     316  	    /* Sign.  */
     317  	    + 2    + 1 + 0 + precision + 1 + 1
     318  	    /* 0x    h   .   hhh         P   ExpoSign.  */
     319  	    + ((expbuf + sizeof expbuf) - expstr));
     320  	    /* Exponent.  */
     321  
     322    /* Count the decimal point.
     323       A special case when the mantissa or the precision is zero and the `#'
     324       is not given.  In this case we must not print the decimal point.  */
     325    if (precision > 0 || info->alt)
     326      --width;
     327  
     328    if (!info->left && info->pad != '0')
     329      __printf_buffer_pad (buf, ' ', width);
     330  
     331    if (negative)
     332      __printf_buffer_putc (buf, '-');
     333    else if (info->showsign)
     334      __printf_buffer_putc (buf, '+');
     335    else if (info->space)
     336      __printf_buffer_putc (buf, ' ');
     337  
     338    __printf_buffer_putc (buf, '0');
     339    if ('X' - 'A' == 'x' - 'a')
     340      __printf_buffer_putc (buf, info->spec + ('x' - 'a'));
     341    else
     342      __printf_buffer_putc (buf, info->spec == 'A' ? 'X' : 'x');
     343  
     344    if (!info->left && info->pad == '0')
     345      __printf_buffer_pad (buf, '0', width);
     346  
     347    __printf_buffer_putc (buf, leading);
     348  
     349    if (precision > 0 || info->alt)
     350      __printf_buffer_puts (buf, decimal);
     351  
     352    if (precision > 0)
     353      {
     354        ssize_t tofill = precision - (numend - numstr);
     355        __printf_buffer_write (buf, numstr, MIN (numend - numstr, precision));
     356        __printf_buffer_pad (buf, '0', tofill);
     357      }
     358  
     359    if ('P' - 'A' == 'p' - 'a')
     360      __printf_buffer_putc (buf, info->spec + ('p' - 'a'));
     361    else
     362      __printf_buffer_putc (buf, info->spec == 'A' ? 'P' : 'p');
     363  
     364    __printf_buffer_putc (buf, expnegative ? '-' : '+');
     365  
     366    __printf_buffer_write (buf, expstr, (expbuf + sizeof expbuf) - expstr);
     367  
     368    if (info->left && info->pad != '0')
     369      __printf_buffer_pad (buf, info->pad, width);
     370  }
     371  
     372  void
     373  __printf_fphex_l_buffer (struct __printf_buffer *buf, locale_t loc,
     374  			 const struct printf_info *info,
     375  			 const void *const *args)
     376  {
     377    __printf_fphex_buffer (buf, _nl_lookup (loc, LC_NUMERIC, DECIMAL_POINT),
     378  			 info, args);
     379  }
     380  
     381  
     382  /* The wide buffer version is implemented by translating the output of
     383     the multibyte version.  */
     384  
     385  struct __printf_buffer_fphex_to_wide
     386  {
     387    struct __printf_buffer base;
     388    wchar_t decimalwc;
     389    struct __wprintf_buffer *next;
     390    char untranslated[PRINTF_BUFFER_SIZE_DIGITS];
     391  };
     392  
     393  /* Translate to wide characters, rewriting "." to the actual decimal
     394     point.  */
     395  void
     396  __printf_buffer_flush_fphex_to_wide (struct __printf_buffer_fphex_to_wide *buf)
     397  {
     398    /* No need to adjust buf->base.written, only buf->next->written matters.  */
     399    for (char *p = buf->untranslated; p < buf->base.write_ptr; ++p)
     400      {
     401        /* wchar_t overlaps with char in the ASCII range.  */
     402        wchar_t ch = *p;
     403        if (ch == L'.')
     404  	ch = buf->decimalwc;
     405        __wprintf_buffer_putc (buf->next, ch);
     406      }
     407  
     408    if (!__wprintf_buffer_has_failed (buf->next))
     409      buf->base.write_ptr = buf->untranslated;
     410    else
     411      __printf_buffer_mark_failed (&buf->base);
     412  }
     413  
     414  void
     415  __wprintf_fphex_l_buffer (struct __wprintf_buffer *next, locale_t loc,
     416  			  const struct printf_info *info,
     417  			  const void *const *args)
     418  {
     419    struct __printf_buffer_fphex_to_wide buf;
     420    __printf_buffer_init (&buf.base, buf.untranslated, sizeof (buf.untranslated),
     421  			__printf_buffer_mode_fphex_to_wide);
     422    buf.decimalwc = _nl_lookup_word (loc, LC_NUMERIC,
     423  				   _NL_NUMERIC_DECIMAL_POINT_WC);
     424    buf.next = next;
     425    __printf_fphex_buffer (&buf.base, ".", info, args);
     426    if (__printf_buffer_has_failed (&buf.base))
     427      {
     428        __wprintf_buffer_mark_failed (buf.next);
     429        return;
     430      }
     431    __printf_buffer_flush_fphex_to_wide (&buf);
     432  }