printf-hook-builtin: Add some preliminary floating point support

This minimalistic implementation has no aspiration for completeness or
accuracy, and just provides what we need.
This commit is contained in:
Martin Willi
2013-10-11 11:06:09 +02:00
parent 7e6a4cdc84
commit edc7a3d02f
2 changed files with 223 additions and 2 deletions
+29
View File
@@ -91,6 +91,31 @@ START_TEST(test_printf_hex)
}
END_TEST
START_TEST(test_printf_float)
{
verify("0.000000", "%f", 0.0);
verify("1.000000", "%f", 1.0);
verify("12345.1", "%.1f", 12345.123);
verify("1", "%.0f", 1.0);
verify("1.4", "%.1f", 1.456789);
verify("1.34", "%.2f", 1.3456789);
verify("1.234", "%.3f", 1.23456789);
verify("1.1234", "%.4f", 1.123456789);
verify("-1.000000", "%f", -1.0);
verify("-12345.1", "%.1f", -12345.123);
verify("-1", "%.0f", -1.0);
verify("-1.4", "%.1f", -1.456789);
verify("-1.34", "%.2f", -1.3456789);
verify("-1.234", "%.3f", -1.23456789);
verify("-1.1234", "%.4f", -1.123456789);
verify(" 1.2", "%5.1f", 1.234);
verify("001.2", "%05.1f", 1.234);
verify("1.2 ", "%-5.1f", 1.234);
}
END_TEST
Suite *printf_suite_create()
{
Suite *s;
@@ -118,5 +143,9 @@ Suite *printf_suite_create()
tcase_add_test(tc, test_printf_hex);
suite_add_tcase(s, tc);
tc = tcase_create("float");
tcase_add_test(tc, test_printf_float);
suite_add_tcase(s, tc);
return s;
}
@@ -47,6 +47,7 @@
#include <stdarg.h>
#include <string.h>
#include <errno.h>
#include <math.h>
#define PRINTF_BUF_LEN 8192
#define ARGS_MAX 3
@@ -222,14 +223,15 @@ typedef enum {
#define EMIT(x) ({ if (o<n){*q++ = (x);} o++; })
static const char lcdigits[] = "0123456789abcdef";
static const char ucdigits[] = "0123456789ABCDEF";
/**
* Write an integer argument to q, using flags, base, width and precision
*/
static size_t format_int(char *q, size_t n, uintmax_t val, bpf_flag_t flags,
int base, int width, int prec)
{
static const char lcdigits[] = "0123456789abcdef";
static const char ucdigits[] = "0123456789ABCDEF";
char *qq;
size_t o = 0, oo;
const char *digits;
@@ -393,6 +395,155 @@ static size_t format_int(char *q, size_t n, uintmax_t val, bpf_flag_t flags,
return o;
}
/**
* Write an double argument to q, using flags, base, width and precision
*/
static size_t format_double(char *q, size_t n, double val, bpf_flag_t flags,
int base, int width, int prec)
{
char *qq;
size_t o = 0, oo;
const char *digits;
uintmax_t tmpval;
int minus = 0;
int ndigits = 0, nchars;
/* Select type of digits */
digits = (flags & FL_UPPER) ? ucdigits : lcdigits;
if (prec < 0)
{
/* default precision */
prec = 6;
}
if (val < 0)
{
minus = 1;
}
tmpval = (uintmax_t)fabs(val);
while (tmpval)
{
tmpval /= base;
ndigits++;
}
if (val == 0)
{
ndigits++;
}
/* Now compute the number of nondigits */
nchars = ndigits;
if (prec)
{
/* Space for decimal-point and digits after that */
nchars += prec + 1;
}
if (minus || (flags & (FL_PLUS | FL_SPACE)))
{
/* Need space for sign */
nchars++;
}
if ((flags & FL_HASH) && base == 16)
{
/* Add 0x for hex */
nchars += 2;
}
/* Emit early space padding */
if (!(flags & (FL_MINUS | FL_ZERO)) && width > nchars)
{
while (width > nchars)
{
EMIT(' ');
width--;
}
}
/* Emit nondigits */
if (minus)
{
EMIT('-');
}
else if (flags & FL_PLUS)
{
EMIT('+');
}
else if (flags & FL_SPACE)
{
EMIT(' ');
}
if ((flags & FL_HASH) && base == 16)
{
EMIT('0');
EMIT((flags & FL_UPPER) ? 'X' : 'x');
}
/* Emit zero padding */
if ((flags & (FL_MINUS | FL_ZERO)) == FL_ZERO && width > ndigits)
{
while (width > nchars)
{
EMIT('0');
width--;
}
}
/* Generate the number. This is done from right to left. */
/* Advance the pointer to end of number */
q += ndigits;
o += ndigits;
/* Temporary values */
qq = q;
oo = o;
tmpval = (uintmax_t)fabs(val);
while (ndigits > 0)
{
qq--;
oo--;
ndigits--;
if (oo < n)
{
*qq = digits[tmpval % base];
}
tmpval /= base;
}
if (prec)
{
EMIT('.');
q += prec;
o += prec;
qq = q;
oo = o;
while (prec > 0)
{
tmpval = (uintmax_t)(fabs(val) * pow(base, prec));
qq--;
oo--;
prec--;
if (oo < n)
{
*qq = digits[tmpval % base];
}
}
}
/* Emit late space padding */
while ((flags & FL_MINUS) && width > nchars)
{
EMIT(' ');
width--;
}
return o;
}
int builtin_vsnprintf(char *buffer, size_t n, const char *format, va_list ap)
{
const char *p = format;
@@ -724,6 +875,47 @@ int builtin_vsnprintf(char *buffer, size_t n, const char *format, va_list ap)
}
break;
}
case 'A':
{
base = 16;
flags |= FL_UPPER;
goto is_double;
}
case 'E':
case 'G':
{
/* currently not supported, fall */
}
case 'F':
{
base = 10;
flags |= FL_UPPER;
goto is_double;
}
case 'a':
{
base = 16;
goto is_double;
}
case 'e':
case 'g':
{
/* currently not supported, fall */
}
case 'f':
{
base = 10;
goto is_double;
}
is_double:
{
sz = format_double(q, (o < n) ? n - o : 0,
va_arg(ap, double),
flags, base, width, prec);
q += sz;
o += sz;
break;
}
case 'n':
{
/* Output the number of characters written */