xref: /netbsd-src/external/gpl3/gcc/dist/libquadmath/printf/printf_fphex.c (revision ccd9df534e375a4366c5b55f23782053c7a98d82)
1 /* Print floating point number in hexadecimal notation according to ISO C99.
2    Copyright (C) 1997-2012 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997.
5 
6    The GNU C Library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Lesser General Public
8    License as published by the Free Software Foundation; either
9    version 2.1 of the License, or (at your option) any later version.
10 
11    The GNU C Library is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Lesser General Public License for more details.
15 
16    You should have received a copy of the GNU Lesser General Public
17    License along with the GNU C Library; if not, see
18    <http://www.gnu.org/licenses/>.  */
19 
20 #include <config.h>
21 #include <math.h>
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include <stdbool.h>
26 #define NDEBUG
27 #include <assert.h>
28 #include "quadmath-rounding-mode.h"
29 #include "quadmath-printf.h"
30 #include "_itoa.h"
31 #include "_itowa.h"
32 
33 
34 /* Macros for doing the actual output.  */
35 
36 #define outchar(ch)							      \
37   do									      \
38     {									      \
39       register const int outc = (ch);					      \
40       if (PUTC (outc, fp) == EOF)					      \
41 	return -1;							      \
42       ++done;								      \
43     } while (0)
44 
45 #define PRINT(ptr, wptr, len)						      \
46   do									      \
47     {									      \
48       register size_t outlen = (len);					      \
49       if (wide)								      \
50 	while (outlen-- > 0)						      \
51 	  outchar (*wptr++);						      \
52       else								      \
53 	while (outlen-- > 0)						      \
54 	  outchar (*ptr++);						      \
55     } while (0)
56 
57 #define PADN(ch, len)							      \
58   do									      \
59     {									      \
60       if (PAD (fp, ch, len) != len)					      \
61 	return -1;							      \
62       done += len;							      \
63     }									      \
64   while (0)
65 
66 
67 
68 int
69 __quadmath_printf_fphex (struct __quadmath_printf_file *fp,
70 			 const struct printf_info *info,
71 			 const void *const *args)
72 {
73   /* The floating-point value to output.  */
74   ieee854_float128 fpnum;
75 
76   /* Locale-dependent representation of decimal point.	*/
77   const char *decimal;
78   wchar_t decimalwc;
79 
80   /* "NaN" or "Inf" for the special cases.  */
81   const char *special = NULL;
82   const wchar_t *wspecial = NULL;
83 
84   /* Buffer for the generated number string for the mantissa.  The
85      maximal size for the mantissa is 128 bits.  */
86   char numbuf[32];
87   char *numstr;
88   char *numend;
89   wchar_t wnumbuf[32];
90   wchar_t *wnumstr;
91   wchar_t *wnumend;
92   int negative;
93 
94   /* The maximal exponent of two in decimal notation has 5 digits.  */
95   char expbuf[5];
96   char *expstr;
97   wchar_t wexpbuf[5];
98   wchar_t *wexpstr;
99   int expnegative;
100   int exponent;
101 
102   /* Non-zero is mantissa is zero.  */
103   int zero_mantissa;
104 
105   /* The leading digit before the decimal point.  */
106   char leading;
107 
108   /* Precision.  */
109   int precision = info->prec;
110 
111   /* Width.  */
112   int width = info->width;
113 
114   /* Number of characters written.  */
115   int done = 0;
116 
117   /* Nonzero if this is output on a wide character stream.  */
118   int wide = info->wide;
119 
120   bool do_round_away;
121 
122   /* Figure out the decimal point character.  */
123 #ifdef USE_NL_LANGINFO
124   if (info->extra == 0)
125     decimal = nl_langinfo (DECIMAL_POINT);
126   else
127     {
128       decimal = nl_langinfo (MON_DECIMAL_POINT);
129       if (*decimal == '\0')
130 	decimal = nl_langinfo (DECIMAL_POINT);
131     }
132   /* The decimal point character must never be zero.  */
133   assert (*decimal != '\0');
134 #elif defined USE_LOCALECONV
135   const struct lconv *lc = localeconv ();
136   if (info->extra == 0)
137     decimal = lc->decimal_point;
138   else
139     {
140       decimal = lc->mon_decimal_point;
141       if (decimal == NULL || *decimal == '\0')
142 	decimal = lc->decimal_point;
143     }
144   if (decimal == NULL || *decimal == '\0')
145     decimal = ".";
146 #else
147   decimal = ".";
148 #endif
149 #ifdef USE_NL_LANGINFO_WC
150   if (info->extra == 0)
151     decimalwc = nl_langinfo_wc (_NL_NUMERIC_DECIMAL_POINT_WC);
152   else
153     {
154       decimalwc = nl_langinfo_wc (_NL_MONETARY_DECIMAL_POINT_WC);
155       if (decimalwc == L_('\0'))
156 	decimalwc = nl_langinfo_wc (_NL_NUMERIC_DECIMAL_POINT_WC);
157     }
158   /* The decimal point character must never be zero.  */
159   assert (decimalwc != L_('\0'));
160 #else
161   decimalwc = L_('.');
162 #endif
163 
164   /* Fetch the argument value.	*/
165     {
166       memcpy (&fpnum.value, *(const void *const *) args[0],
167 	      sizeof (fpnum.value));
168 
169       /* Check for special values: not a number or infinity.  */
170       if (isnanq (fpnum.value))
171 	{
172 	  negative = fpnum.ieee.negative != 0;
173 	  if (isupper (info->spec))
174 	    {
175 	      special = "NAN";
176 	      wspecial = L_("NAN");
177 	    }
178 	  else
179 	    {
180 	      special = "nan";
181 	      wspecial = L_("nan");
182 	    }
183 	}
184       else
185 	{
186 	  if (isinfq (fpnum.value))
187 	    {
188 	      if (isupper (info->spec))
189 		{
190 		  special = "INF";
191 		  wspecial = L_("INF");
192 		}
193 	      else
194 		{
195 		  special = "inf";
196 		  wspecial = L_("inf");
197 		}
198 	    }
199 
200 	  negative = signbitq (fpnum.value);
201 	}
202     }
203 
204   if (special)
205     {
206       int width = info->width;
207 
208       if (negative || info->showsign || info->space)
209 	--width;
210       width -= 3;
211 
212       if (!info->left && width > 0)
213 	PADN (' ', width);
214 
215       if (negative)
216 	outchar ('-');
217       else if (info->showsign)
218 	outchar ('+');
219       else if (info->space)
220 	outchar (' ');
221 
222       PRINT (special, wspecial, 3);
223 
224       if (info->left && width > 0)
225 	PADN (' ', width);
226 
227       return done;
228     }
229 
230     {
231       /* We have 112 bits of mantissa plus one implicit digit.  Since
232 	 112 bits are representable without rest using hexadecimal
233 	 digits we use only the implicit digits for the number before
234 	 the decimal point.  */
235       uint64_t num0, num1;
236 
237       assert (sizeof (long double) == 16);
238 
239       num0 = (((unsigned long long int) fpnum.ieee.mantissa0) << 32
240 	      | fpnum.ieee.mantissa1);
241       num1 = (((unsigned long long int) fpnum.ieee.mantissa2) << 32
242 	      | fpnum.ieee.mantissa3);
243 
244       zero_mantissa = (num0|num1) == 0;
245 
246       if (sizeof (unsigned long int) > 6)
247 	{
248 	  numstr = _itoa_word (num1, numbuf + sizeof numbuf, 16,
249 			       info->spec == 'A');
250 	  wnumstr = _itowa_word (num1,
251 				 wnumbuf + sizeof (wnumbuf) / sizeof (wchar_t),
252 				 16, info->spec == 'A');
253 	}
254       else
255 	{
256 	  numstr = _itoa (num1, numbuf + sizeof numbuf, 16,
257 			  info->spec == 'A');
258 	  wnumstr = _itowa (num1,
259 			    wnumbuf + sizeof (wnumbuf) / sizeof (wchar_t),
260 			    16, info->spec == 'A');
261 	}
262 
263       while (numstr > numbuf + (sizeof numbuf - 64 / 4))
264 	{
265 	  *--numstr = '0';
266 	  *--wnumstr = L_('0');
267 	}
268 
269       if (sizeof (unsigned long int) > 6)
270 	{
271 	  numstr = _itoa_word (num0, numstr, 16, info->spec == 'A');
272 	  wnumstr = _itowa_word (num0, wnumstr, 16, info->spec == 'A');
273 	}
274       else
275 	{
276 	  numstr = _itoa (num0, numstr, 16, info->spec == 'A');
277 	  wnumstr = _itowa (num0, wnumstr, 16, info->spec == 'A');
278 	}
279 
280       /* Fill with zeroes.  */
281       while (numstr > numbuf + (sizeof numbuf - 112 / 4))
282 	{
283 	  *--wnumstr = L_('0');
284 	  *--numstr = '0';
285 	}
286 
287       leading = fpnum.ieee.exponent == 0 ? '0' : '1';
288 
289       exponent = fpnum.ieee.exponent;
290 
291       if (exponent == 0)
292 	{
293 	  if (zero_mantissa)
294 	    expnegative = 0;
295 	  else
296 	    {
297 	      /* This is a denormalized number.  */
298 	      expnegative = 1;
299 	      exponent = IEEE854_FLOAT128_BIAS - 1;
300 	    }
301 	}
302       else if (exponent >= IEEE854_FLOAT128_BIAS)
303 	{
304 	  expnegative = 0;
305 	  exponent -= IEEE854_FLOAT128_BIAS;
306 	}
307       else
308 	{
309 	  expnegative = 1;
310 	  exponent = -(exponent - IEEE854_FLOAT128_BIAS);
311 	}
312     }
313 
314   /* Look for trailing zeroes.  */
315   if (! zero_mantissa)
316     {
317       wnumend = &wnumbuf[sizeof wnumbuf / sizeof wnumbuf[0]];
318       numend = &numbuf[sizeof numbuf / sizeof numbuf[0]];
319       while (wnumend[-1] == L_('0'))
320 	{
321 	  --wnumend;
322 	  --numend;
323 	}
324 
325       do_round_away = false;
326 
327       if (precision != -1 && precision < numend - numstr)
328 	{
329 	  char last_digit = precision > 0 ? numstr[precision - 1] : leading;
330 	  char next_digit = numstr[precision];
331 	  int last_digit_value = (last_digit >= 'A' && last_digit <= 'F'
332 				  ? last_digit - 'A' + 10
333 				  : (last_digit >= 'a' && last_digit <= 'f'
334 				     ? last_digit - 'a' + 10
335 				     : last_digit - '0'));
336 	  int next_digit_value = (next_digit >= 'A' && next_digit <= 'F'
337 				  ? next_digit - 'A' + 10
338 				  : (next_digit >= 'a' && next_digit <= 'f'
339 				     ? next_digit - 'a' + 10
340 				     : next_digit - '0'));
341 	  bool more_bits = ((next_digit_value & 7) != 0
342 			    || precision + 1 < numend - numstr);
343 #ifdef HAVE_FENV_H
344 	  int rounding_mode = get_rounding_mode ();
345 	  do_round_away = round_away (negative, last_digit_value & 1,
346 				      next_digit_value >= 8, more_bits,
347 				      rounding_mode);
348 #endif
349 	}
350 
351       if (precision == -1)
352 	precision = numend - numstr;
353       else if (do_round_away)
354 	{
355 	  /* Round up.  */
356 	  int cnt = precision;
357 	  while (--cnt >= 0)
358 	    {
359 	      char ch = numstr[cnt];
360 	      /* We assume that the digits and the letters are ordered
361 		 like in ASCII.  This is true for the rest of GNU, too.  */
362 	      if (ch == '9')
363 		{
364 		  wnumstr[cnt] = (wchar_t) info->spec;
365 		  numstr[cnt] = info->spec;	/* This is tricky,
366 		  				   think about it!  */
367 		  break;
368 		}
369 	      else if (tolower (ch) < 'f')
370 		{
371 		  ++numstr[cnt];
372 		  ++wnumstr[cnt];
373 		  break;
374 		}
375 	      else
376 		{
377 		  numstr[cnt] = '0';
378 		  wnumstr[cnt] = L_('0');
379 		}
380 	    }
381 	  if (cnt < 0)
382 	    {
383 	      /* The mantissa so far was fff...f  Now increment the
384 		 leading digit.  Here it is again possible that we
385 		 get an overflow.  */
386 	      if (leading == '9')
387 		leading = info->spec;
388 	      else if (tolower (leading) < 'f')
389 		++leading;
390 	      else
391 		{
392 		  leading = '1';
393 		  if (expnegative)
394 		    {
395 		      exponent -= 4;
396 		      if (exponent <= 0)
397 			{
398 			  exponent = -exponent;
399 			  expnegative = 0;
400 			}
401 		    }
402 		  else
403 		    exponent += 4;
404 		}
405 	    }
406 	}
407     }
408   else
409     {
410       if (precision == -1)
411 	precision = 0;
412       numend = numstr;
413       wnumend = wnumstr;
414     }
415 
416   /* Now we can compute the exponent string.  */
417   expstr = _itoa_word (exponent, expbuf + sizeof expbuf, 10, 0);
418   wexpstr = _itowa_word (exponent,
419 			 wexpbuf + sizeof wexpbuf / sizeof (wchar_t), 10, 0);
420 
421   /* Now we have all information to compute the size.  */
422   width -= ((negative || info->showsign || info->space)
423 	    /* Sign.  */
424 	    + 2    + 1 + 0 + precision + 1 + 1
425 	    /* 0x    h   .   hhh         P   ExpoSign.  */
426 	    + ((expbuf + sizeof expbuf) - expstr));
427 	    /* Exponent.  */
428 
429   /* Count the decimal point.
430      A special case when the mantissa or the precision is zero and the `#'
431      is not given.  In this case we must not print the decimal point.  */
432   if (precision > 0 || info->alt)
433     width -= wide ? 1 : strlen (decimal);
434 
435   if (!info->left && info->pad != '0' && width > 0)
436     PADN (' ', width);
437 
438   if (negative)
439     outchar ('-');
440   else if (info->showsign)
441     outchar ('+');
442   else if (info->space)
443     outchar (' ');
444 
445   outchar ('0');
446   if ('X' - 'A' == 'x' - 'a')
447     outchar (info->spec + ('x' - 'a'));
448   else
449     outchar (info->spec == 'A' ? 'X' : 'x');
450 
451   if (!info->left && info->pad == '0' && width > 0)
452     PADN ('0', width);
453 
454   outchar (leading);
455 
456   if (precision > 0 || info->alt)
457     {
458       const wchar_t *wtmp = &decimalwc;
459       PRINT (decimal, wtmp, wide ? 1 : strlen (decimal));
460     }
461 
462   if (precision > 0)
463     {
464       ssize_t tofill = precision - (numend - numstr);
465       PRINT (numstr, wnumstr, MIN (numend - numstr, precision));
466       if (tofill > 0)
467 	PADN ('0', tofill);
468     }
469 
470   if ('P' - 'A' == 'p' - 'a')
471     outchar (info->spec + ('p' - 'a'));
472   else
473     outchar (info->spec == 'A' ? 'P' : 'p');
474 
475   outchar (expnegative ? '-' : '+');
476 
477   PRINT (expstr, wexpstr, (expbuf + sizeof expbuf) - expstr);
478 
479   if (info->left && info->pad != '0' && width > 0)
480     PADN (info->pad, width);
481 
482   return done;
483 }
484