blob: 78e52e9a2ad35496748eac24c061a1da19e3c614 [file] [log] [blame]
Ed Warnickecb9cada2015-12-08 15:45:58 -07001/*
2 * Copyright (c) 2015 Cisco and/or its affiliates.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at:
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15/*------------------------------------------------------------------
16 * format.c -- see notice below
17 *
18 * October 2003, Eliot Dresselhaus
19 *
20 * Modifications to this file Copyright (c) 2003 by cisco Systems, Inc.
21 * All rights reserved.
22 *------------------------------------------------------------------
23 */
24
25/*
26 Copyright (c) 2001, 2002, 2003, 2006 Eliot Dresselhaus
27
28 Permission is hereby granted, free of charge, to any person obtaining
29 a copy of this software and associated documentation files (the
30 "Software"), to deal in the Software without restriction, including
31 without limitation the rights to use, copy, modify, merge, publish,
32 distribute, sublicense, and/or sell copies of the Software, and to
33 permit persons to whom the Software is furnished to do so, subject to
34 the following conditions:
35
36 The above copyright notice and this permission notice shall be
37 included in all copies or substantial portions of the Software.
38
39 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
40 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
41 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
42 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
43 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
44 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
45 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
46*/
47
Dave Barachc3799992016-08-15 11:12:27 -040048#include <stdarg.h> /* va_start, etc */
Ed Warnickecb9cada2015-12-08 15:45:58 -070049
50#ifdef CLIB_UNIX
51#include <unistd.h>
52#include <stdio.h>
53#endif
54
55#ifdef CLIB_STANDALONE
56#include <vppinfra/standalone_stdio.h>
57#endif
58
59#include <vppinfra/mem.h>
60#include <vppinfra/format.h>
61#include <vppinfra/vec.h>
62#include <vppinfra/error.h>
63#include <vppinfra/string.h>
Dave Barachc3799992016-08-15 11:12:27 -040064#include <vppinfra/os.h> /* os_puts */
Ed Warnickecb9cada2015-12-08 15:45:58 -070065
Dave Barachc3799992016-08-15 11:12:27 -040066typedef struct
67{
68 /* Output number in this base. */
69 u8 base;
Ed Warnickecb9cada2015-12-08 15:45:58 -070070
Dave Barachc3799992016-08-15 11:12:27 -040071 /* Number of show of 64 bit number. */
72 u8 n_bits;
Ed Warnickecb9cada2015-12-08 15:45:58 -070073
Dave Barachc3799992016-08-15 11:12:27 -040074 /* Signed or unsigned. */
75 u8 is_signed;
Ed Warnickecb9cada2015-12-08 15:45:58 -070076
Dave Barachc3799992016-08-15 11:12:27 -040077 /* Output digits uppercase (not lowercase) %X versus %x. */
78 u8 uppercase_digits;
Ed Warnickecb9cada2015-12-08 15:45:58 -070079} format_integer_options_t;
80
Dave Barachc3799992016-08-15 11:12:27 -040081static u8 *format_integer (u8 * s, u64 number,
82 format_integer_options_t * options);
83static u8 *format_float (u8 * s, f64 x, uword n_digits_to_print,
84 uword output_style);
Ed Warnickecb9cada2015-12-08 15:45:58 -070085
Dave Barachc3799992016-08-15 11:12:27 -040086typedef struct
87{
Ed Warnickecb9cada2015-12-08 15:45:58 -070088 /* String justification: + => right, - => left, = => center. */
89 uword justify;
90
91 /* Width of string (before and after decimal point for numbers).
92 0 => natural width. */
93 uword width[2];
94
95 /* Long => 'l', long long 'L', int 0. */
96 uword how_long;
97
98 /* Pad character. Defaults to space. */
99 uword pad_char;
100} format_info_t;
101
Dave Barachc3799992016-08-15 11:12:27 -0400102static u8 *
103justify (u8 * s, format_info_t * fi, uword s_len_orig)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700104{
105 uword i0, l0, l1;
106
107 i0 = s_len_orig;
108 l0 = i0 + fi->width[0];
109 l1 = vec_len (s);
110
111 /* If width is zero user returned width. */
112 if (l0 == i0)
113 l0 = l1;
114
115 if (l1 > l0)
116 _vec_len (s) = l0;
117 else if (l0 > l1)
118 {
119 uword n = l0 - l1;
120 uword n_left = 0, n_right = 0;
121
122 switch (fi->justify)
123 {
124 case '-':
125 n_right = n;
126 break;
127
128 case '+':
Dave Barachc3799992016-08-15 11:12:27 -0400129 n_left = n;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700130 break;
131
132 case '=':
Dave Barachc3799992016-08-15 11:12:27 -0400133 n_right = n_left = n / 2;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700134 if (n % 2)
135 n_left++;
136 break;
137 }
138 if (n_left > 0)
139 {
140 vec_insert (s, n_left, i0);
141 memset (s + i0, fi->pad_char, n_left);
142 l1 = vec_len (s);
143 }
144 if (n_right > 0)
145 {
146 vec_resize (s, n_right);
147 memset (s + l1, fi->pad_char, n_right);
148 }
149 }
150 return s;
151}
152
Dave Barachc3799992016-08-15 11:12:27 -0400153static u8 *
154do_percent (u8 ** _s, u8 * fmt, va_list * va)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700155{
Dave Barachc3799992016-08-15 11:12:27 -0400156 u8 *s = *_s;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700157 uword c;
158
Dave Barachc3799992016-08-15 11:12:27 -0400159 u8 *f = fmt;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700160
161 format_info_t fi = {
162 .justify = '+',
163 .width = {0},
164 .pad_char = ' ',
165 .how_long = 0,
166 };
167
168 uword i;
169
170 ASSERT (f[0] == '%');
171
172 switch (c = *++f)
173 {
174 case '%':
175 /* %% => % */
176 vec_add1 (s, c);
177 f++;
178 goto done;
179
180 case '-':
181 case '+':
182 case '=':
183 fi.justify = c;
184 c = *++f;
185 break;
186 }
187
188 /* Parse width0 . width1. */
189 {
190 uword is_first_digit = 1;
191
192 fi.width[0] = fi.width[1] = 0;
193 for (i = 0; i < 2; i++)
194 {
195 if (c == '0' && i == 0 && is_first_digit)
196 fi.pad_char = '0';
197 is_first_digit = 0;
Dave Barachc3799992016-08-15 11:12:27 -0400198 if (c == '*')
199 {
200 fi.width[i] = va_arg (*va, int);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700201 c = *++f;
Dave Barachc3799992016-08-15 11:12:27 -0400202 }
203 else
204 {
205 while (c >= '0' && c <= '9')
206 {
207 fi.width[i] = 10 * fi.width[i] + (c - '0');
Ed Warnickecb9cada2015-12-08 15:45:58 -0700208 c = *++f;
Dave Barachc3799992016-08-15 11:12:27 -0400209 }
210 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700211 if (c != '.')
212 break;
213 c = *++f;
214 }
215 }
216
217 /* Parse %l* and %L* */
218 switch (c)
219 {
220 case 'w':
221 /* word format. */
222 fi.how_long = 'w';
223 c = *++f;
224 break;
225
226 case 'L':
227 case 'l':
228 fi.how_long = c;
229 c = *++f;
230 if (c == 'l' && *f == 'l')
231 {
232 fi.how_long = 'L';
233 c = *++f;
234 }
235 break;
236 }
237
238 /* Finally we are ready for format letter. */
239 if (c != 0)
240 {
241 uword s_initial_len = vec_len (s);
242 format_integer_options_t o = {
243 .is_signed = 0,
244 .base = 10,
245 .n_bits = BITS (uword),
246 .uppercase_digits = 0,
247 };
248
249 f++;
250
251 switch (c)
252 {
Dave Barachc3799992016-08-15 11:12:27 -0400253 default:
254 {
255 /* Try to give a helpful error message. */
256 vec_free (s);
257 s = format (s, "**** CLIB unknown format `%%%c' ****", c);
258 goto done;
259 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700260
261 case 'c':
262 vec_add1 (s, va_arg (*va, int));
263 break;
264
265 case 'p':
266 vec_add1 (s, '0');
267 vec_add1 (s, 'x');
268
269 o.is_signed = 0;
270 o.n_bits = BITS (uword *);
271 o.base = 16;
272 o.uppercase_digits = 0;
273
274 s = format_integer (s, pointer_to_uword (va_arg (*va, void *)), &o);
275 break;
276
277 case 'x':
278 case 'X':
279 case 'u':
280 case 'd':
281 {
282 u64 number;
283
284 o.base = 10;
285 if (c == 'x' || c == 'X')
286 o.base = 16;
287 o.is_signed = c == 'd';
288 o.uppercase_digits = c == 'X';
289
290 switch (fi.how_long)
291 {
292 case 'L':
293 number = va_arg (*va, unsigned long long);
294 o.n_bits = BITS (unsigned long long);
295 break;
296
297 case 'l':
298 number = va_arg (*va, long);
299 o.n_bits = BITS (long);
300 break;
301
302 case 'w':
303 number = va_arg (*va, word);
304 o.n_bits = BITS (uword);
305 break;
306
307 default:
308 number = va_arg (*va, int);
309 o.n_bits = BITS (int);
310 break;
311 }
312
313 s = format_integer (s, number, &o);
314 }
315 break;
316
317 case 's':
318 case 'S':
319 {
Dave Barachc3799992016-08-15 11:12:27 -0400320 char *cstring = va_arg (*va, char *);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700321 uword len;
322
Dave Barachc3799992016-08-15 11:12:27 -0400323 if (!cstring)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700324 {
325 cstring = "(nil)";
326 len = 5;
327 }
328 else if (fi.width[1] != 0)
329 len = clib_min (strlen (cstring), fi.width[1]);
330 else
331 len = strlen (cstring);
Dave Barachc3799992016-08-15 11:12:27 -0400332
Ed Warnickecb9cada2015-12-08 15:45:58 -0700333 /* %S => format string as C identifier (replace _ with space). */
334 if (c == 'S')
335 {
336 for (i = 0; i < len; i++)
337 vec_add1 (s, cstring[i] == '_' ? ' ' : cstring[i]);
Dave Barachc3799992016-08-15 11:12:27 -0400338 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700339 else
340 vec_add (s, cstring, len);
341 }
342 break;
343
344 case 'v':
345 {
Dave Barachc3799992016-08-15 11:12:27 -0400346 u8 *v = va_arg (*va, u8 *);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700347 uword len;
348
349 if (fi.width[1] != 0)
350 len = clib_min (vec_len (v), fi.width[1]);
351 else
352 len = vec_len (v);
353
354 vec_add (s, v, len);
355 }
356 break;
357
Dave Barachc3799992016-08-15 11:12:27 -0400358 case 'f':
359 case 'g':
360 case 'e':
Ed Warnickecb9cada2015-12-08 15:45:58 -0700361 /* Floating point. */
362 ASSERT (fi.how_long == 0 || fi.how_long == 'l');
Dave Barachc3799992016-08-15 11:12:27 -0400363 s = format_float (s, va_arg (*va, double), fi.width[1], c);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700364 break;
365
366 case 'U':
367 /* User defined function. */
368 {
Dave Barachc3799992016-08-15 11:12:27 -0400369 typedef u8 *(user_func_t) (u8 * s, va_list * args);
370 user_func_t *u = va_arg (*va, user_func_t *);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700371
Dave Barachc3799992016-08-15 11:12:27 -0400372 s = (*u) (s, va);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700373 }
374 break;
375 }
376
377 s = justify (s, &fi, s_initial_len);
378 }
379
Dave Barachc3799992016-08-15 11:12:27 -0400380done:
Ed Warnickecb9cada2015-12-08 15:45:58 -0700381 *_s = s;
382 return f;
383}
384
Dave Barachc3799992016-08-15 11:12:27 -0400385u8 *
Neale Ranns0bfe5d82016-08-25 15:29:12 +0100386va_format (u8 * s, const char *fmt, va_list * va)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700387{
Dave Barachc3799992016-08-15 11:12:27 -0400388 u8 *f = (u8 *) fmt, *g;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700389 u8 c;
390
391 g = f;
392 while (1)
393 {
394 c = *f;
395
Dave Barachc3799992016-08-15 11:12:27 -0400396 if (!c)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700397 break;
398
399 if (c == '%')
400 {
401 if (f > g)
402 vec_add (s, g, f - g);
403 f = g = do_percent (&s, f, va);
404 }
405 else
406 {
407 f++;
408 }
409 }
410
411 if (f > g)
412 vec_add (s, g, f - g);
413
414 return s;
415}
416
Dave Barachc3799992016-08-15 11:12:27 -0400417u8 *
Neale Ranns0bfe5d82016-08-25 15:29:12 +0100418format (u8 * s, const char *fmt, ...)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700419{
420 va_list va;
421 va_start (va, fmt);
422 s = va_format (s, fmt, &va);
423 va_end (va);
424 return s;
425}
426
Dave Barachc3799992016-08-15 11:12:27 -0400427word
428va_fformat (FILE * f, char *fmt, va_list * va)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700429{
430 word ret;
Dave Barachc3799992016-08-15 11:12:27 -0400431 u8 *s;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700432
433 s = va_format (0, fmt, va);
434
435#ifdef CLIB_UNIX
436 if (f)
437 {
438 ret = fwrite (s, vec_len (s), 1, f);
439 }
440 else
441#endif /* CLIB_UNIX */
442 {
443 ret = 0;
444 os_puts (s, vec_len (s), /* is_error */ 0);
445 }
446
447 vec_free (s);
448 return ret;
449}
450
Dave Barachc3799992016-08-15 11:12:27 -0400451word
452fformat (FILE * f, char *fmt, ...)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700453{
Dave Barachc3799992016-08-15 11:12:27 -0400454 va_list va;
455 word ret;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700456
Dave Barachc3799992016-08-15 11:12:27 -0400457 va_start (va, fmt);
458 ret = va_fformat (f, fmt, &va);
459 va_end (va);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700460
Dave Barachc3799992016-08-15 11:12:27 -0400461 return (ret);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700462}
463
464#ifdef CLIB_UNIX
Dave Barachc3799992016-08-15 11:12:27 -0400465word
466fdformat (int fd, char *fmt, ...)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700467{
468 word ret;
Dave Barachc3799992016-08-15 11:12:27 -0400469 u8 *s;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700470 va_list va;
471
472 va_start (va, fmt);
473 s = va_format (0, fmt, &va);
474 va_end (va);
475
476 ret = write (fd, s, vec_len (s));
477 vec_free (s);
478 return ret;
479}
480#endif
481
482/* Format integral type. */
Dave Barachc3799992016-08-15 11:12:27 -0400483static u8 *
484format_integer (u8 * s, u64 number, format_integer_options_t * options)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700485{
486 u64 q;
487 u32 r;
488 u8 digit_buffer[128];
Dave Barachc3799992016-08-15 11:12:27 -0400489 u8 *d = digit_buffer + sizeof (digit_buffer);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700490 word c, base;
491
492 if (options->is_signed && (i64) number < 0)
493 {
494 number = -number;
495 vec_add1 (s, '-');
496 }
497
498 if (options->n_bits < BITS (number))
499 number &= ((u64) 1 << options->n_bits) - 1;
500
501 base = options->base;
502
503 while (1)
504 {
505 q = number / base;
506 r = number % base;
507
Dave Barachc3799992016-08-15 11:12:27 -0400508 if (r < 10 + 26 + 26)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700509 {
510 if (r < 10)
511 c = '0' + r;
Dave Barachc3799992016-08-15 11:12:27 -0400512 else if (r < 10 + 26)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700513 c = 'a' + (r - 10);
514 else
515 c = 'A' + (r - 10 - 26);
516
517 if (options->uppercase_digits
Dave Barachc3799992016-08-15 11:12:27 -0400518 && base <= 10 + 26 && c >= 'a' && c <= 'z')
Ed Warnickecb9cada2015-12-08 15:45:58 -0700519 c += 'A' - 'a';
520
521 *--d = c;
522 }
Dave Barachc3799992016-08-15 11:12:27 -0400523 else /* will never happen, warning be gone */
524 {
525 *--d = '?';
526 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700527
528 if (q == 0)
529 break;
530
531 number = q;
532 }
533
534 vec_add (s, d, digit_buffer + sizeof (digit_buffer) - d);
535 return s;
536}
537
538/* Floating point formatting. */
539/* Deconstruct IEEE 64 bit number into sign exponent and fraction. */
540#define f64_down(f,sign,expon,fraction) \
541do { \
542 union { u64 u; f64 f; } _f64_down_tmp; \
543 _f64_down_tmp.f = (f); \
544 (sign) = (_f64_down_tmp.u >> 63); \
545 (expon) = ((_f64_down_tmp.u >> 52) & 0x7ff) - 1023; \
546 (fraction) = ((_f64_down_tmp.u << 12) >> 12) | ((u64) 1 << 52); \
547} while (0)
548
549/* Construct IEEE 64 bit number. */
Dave Barachc3799992016-08-15 11:12:27 -0400550static f64
551f64_up (uword sign, word expon, u64 fraction)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700552{
Dave Barachc3799992016-08-15 11:12:27 -0400553 union
554 {
555 u64 u;
556 f64 f;
557 } tmp;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700558
559 tmp.u = (u64) ((sign) != 0) << 63;
560
561 expon += 1023;
562 if (expon > 1023)
563 expon = 1023;
564 if (expon < 0)
565 expon = 0;
566 tmp.u |= (u64) expon << 52;
567
568 tmp.u |= fraction & (((u64) 1 << 52) - 1);
569
570 return tmp.f;
571}
572
573/* Returns approximate precision of number given its exponent. */
Dave Barachc3799992016-08-15 11:12:27 -0400574static f64
575f64_precision (int base2_expon)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700576{
577 static int n_bits = 0;
578
Dave Barachc3799992016-08-15 11:12:27 -0400579 if (!n_bits)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700580 {
581 /* Compute number of significant bits in floating point representation. */
582 f64 one = 0;
583 f64 small = 1;
584
585 while (one != 1)
586 {
587 small *= .5;
588 n_bits++;
589 one = 1 + small;
590 }
591 }
592
593 return f64_up (0, base2_expon - n_bits, 0);
594}
595
596/* Return x 10^n */
Dave Barachc3799992016-08-15 11:12:27 -0400597static f64
598times_power_of_ten (f64 x, int n)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700599{
600 if (n >= 0)
601 {
602 static f64 t[8] = { 1e+0, 1e+1, 1e+2, 1e+3, 1e+4, 1e+5, 1e+6, 1e+7, };
603 while (n >= 8)
604 {
605 x *= 1e+8;
606 n -= 8;
607 }
608 return x * t[n];
609 }
610 else
611 {
612 static f64 t[8] = { 1e-0, 1e-1, 1e-2, 1e-3, 1e-4, 1e-5, 1e-6, 1e-7, };
613 while (n <= -8)
614 {
615 x *= 1e-8;
616 n += 8;
617 }
618 return x * t[-n];
619 }
Dave Barachc3799992016-08-15 11:12:27 -0400620
Ed Warnickecb9cada2015-12-08 15:45:58 -0700621}
622
623/* Write x = y * 10^expon with 1 < y < 10. */
Dave Barachc3799992016-08-15 11:12:27 -0400624static f64
625normalize (f64 x, word * expon_return, f64 * prec_return)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700626{
627 word expon2, expon10;
628 CLIB_UNUSED (u64 fraction);
629 CLIB_UNUSED (word sign);
630 f64 prec;
631
632 f64_down (x, sign, expon2, fraction);
633
Dave Barachc3799992016-08-15 11:12:27 -0400634 expon10 =
635 .5 +
636 expon2 * .301029995663981195213738894724493 /* Log (2) / Log (10) */ ;
637
Ed Warnickecb9cada2015-12-08 15:45:58 -0700638 prec = f64_precision (expon2);
639 x = times_power_of_ten (x, -expon10);
640 prec = times_power_of_ten (prec, -expon10);
641
642 while (x < 1)
643 {
644 x *= 10;
645 prec *= 10;
646 expon10--;
647 }
648
649 while (x > 10)
650 {
651 x *= .1;
652 prec *= .1;
653 expon10++;
654 }
655
656 if (x + prec >= 10)
657 {
658 x = 1;
659 expon10++;
660 }
661
662 *expon_return = expon10;
663 *prec_return = prec;
664
665 return x;
666}
667
Dave Barachc3799992016-08-15 11:12:27 -0400668static u8 *
669add_some_zeros (u8 * s, uword n_zeros)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700670{
671 while (n_zeros > 0)
672 {
673 vec_add1 (s, '0');
674 n_zeros--;
675 }
676 return s;
677}
678
679/* Format a floating point number with the given number of fractional
680 digits (e.g. 1.2345 with 2 fraction digits yields "1.23") and output style. */
681static u8 *
Dave Barachc3799992016-08-15 11:12:27 -0400682format_float (u8 * s, f64 x, uword n_fraction_digits, uword output_style)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700683{
684 f64 prec;
685 word sign, expon, n_fraction_done, added_decimal_point;
686 /* Position of decimal point relative to where we are. */
687 word decimal_point;
688
689 /* Default number of digits to print when its not specified. */
690 if (n_fraction_digits == ~0)
691 n_fraction_digits = 7;
692 n_fraction_done = 0;
693 decimal_point = 0;
694 added_decimal_point = 0;
695 sign = expon = 0;
696
697 /* Special case: zero. */
698 if (x == 0)
699 {
700 do_zero:
701 vec_add1 (s, '0');
702 goto done;
703 }
Dave Barachc3799992016-08-15 11:12:27 -0400704
Ed Warnickecb9cada2015-12-08 15:45:58 -0700705 if (x < 0)
706 {
707 x = -x;
708 sign = 1;
709 }
710
711 /* Check for infinity. */
Dave Barachc3799992016-08-15 11:12:27 -0400712 if (x == x / 2)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700713 return format (s, "%cinfinity", sign ? '-' : '+');
714
715 x = normalize (x, &expon, &prec);
716
717 /* Not enough digits to print anything: so just print 0 */
Dave Barachc3799992016-08-15 11:12:27 -0400718 if ((word) - expon > (word) n_fraction_digits
Ed Warnickecb9cada2015-12-08 15:45:58 -0700719 && (output_style == 'f' || (output_style == 'g')))
720 goto do_zero;
721
722 if (sign)
723 vec_add1 (s, '-');
724
725 if (output_style == 'f'
726 || (output_style == 'g' && expon > -10 && expon < 10))
727 {
728 if (expon < 0)
729 {
730 /* Add decimal point and leading zeros. */
731 vec_add1 (s, '.');
732 n_fraction_done = clib_min (-(expon + 1), n_fraction_digits);
733 s = add_some_zeros (s, n_fraction_done);
734 decimal_point = -n_fraction_done;
735 added_decimal_point = 1;
736 }
737 else
738 decimal_point = expon + 1;
739 }
740 else
741 {
742 /* Exponential output style. */
743 decimal_point = 1;
744 output_style = 'e';
745 }
746
747 while (1)
748 {
749 uword digit;
750
751 /* Number is smaller than precision: call it zero. */
752 if (x < prec)
753 break;
754
755 digit = x;
756 x -= digit;
757 if (x + prec >= 1)
758 {
759 digit++;
760 x -= 1;
761 }
762
763 /* Round last printed digit. */
764 if (decimal_point <= 0
Dave Barachc3799992016-08-15 11:12:27 -0400765 && n_fraction_done + 1 == n_fraction_digits && digit < 9)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700766 digit += x >= .5;
767
768 vec_add1 (s, '0' + digit);
769
770 /* Move rightwards towards/away from decimal point. */
771 decimal_point--;
772
773 n_fraction_done += decimal_point < 0;
Dave Barachc3799992016-08-15 11:12:27 -0400774 if (decimal_point <= 0 && n_fraction_done >= n_fraction_digits)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700775 break;
776
777 if (decimal_point == 0 && x != 0)
778 {
779 vec_add1 (s, '.');
780 added_decimal_point = 1;
781 }
782
783 x *= 10;
784 prec *= 10;
785 }
Dave Barachc3799992016-08-15 11:12:27 -0400786
787done:
Ed Warnickecb9cada2015-12-08 15:45:58 -0700788 if (decimal_point > 0)
789 {
790 s = add_some_zeros (s, decimal_point);
791 decimal_point = 0;
792 }
793
794 if (n_fraction_done < n_fraction_digits)
795 {
Dave Barachc3799992016-08-15 11:12:27 -0400796 if (!added_decimal_point)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700797 vec_add1 (s, '.');
798 s = add_some_zeros (s, n_fraction_digits - n_fraction_done);
799 }
800
801 if (output_style == 'e')
802 s = format (s, "e%wd", expon);
803
804 return s;
805}
806
Dave Barachc3799992016-08-15 11:12:27 -0400807
808/*
809 * fd.io coding-style-patch-verification: ON
810 *
811 * Local Variables:
812 * eval: (c-set-style "gnu")
813 * End:
814 */