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:
@@ -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 */
|
||||
|
||||
Reference in New Issue
Block a user