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
|
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 *printf_suite_create()
|
||||||
{
|
{
|
||||||
Suite *s;
|
Suite *s;
|
||||||
@@ -118,5 +143,9 @@ Suite *printf_suite_create()
|
|||||||
tcase_add_test(tc, test_printf_hex);
|
tcase_add_test(tc, test_printf_hex);
|
||||||
suite_add_tcase(s, tc);
|
suite_add_tcase(s, tc);
|
||||||
|
|
||||||
|
tc = tcase_create("float");
|
||||||
|
tcase_add_test(tc, test_printf_float);
|
||||||
|
suite_add_tcase(s, tc);
|
||||||
|
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,6 +47,7 @@
|
|||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
#define PRINTF_BUF_LEN 8192
|
#define PRINTF_BUF_LEN 8192
|
||||||
#define ARGS_MAX 3
|
#define ARGS_MAX 3
|
||||||
@@ -222,14 +223,15 @@ typedef enum {
|
|||||||
|
|
||||||
#define EMIT(x) ({ if (o<n){*q++ = (x);} o++; })
|
#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
|
* 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,
|
static size_t format_int(char *q, size_t n, uintmax_t val, bpf_flag_t flags,
|
||||||
int base, int width, int prec)
|
int base, int width, int prec)
|
||||||
{
|
{
|
||||||
static const char lcdigits[] = "0123456789abcdef";
|
|
||||||
static const char ucdigits[] = "0123456789ABCDEF";
|
|
||||||
char *qq;
|
char *qq;
|
||||||
size_t o = 0, oo;
|
size_t o = 0, oo;
|
||||||
const char *digits;
|
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;
|
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)
|
int builtin_vsnprintf(char *buffer, size_t n, const char *format, va_list ap)
|
||||||
{
|
{
|
||||||
const char *p = format;
|
const char *p = format;
|
||||||
@@ -724,6 +875,47 @@ int builtin_vsnprintf(char *buffer, size_t n, const char *format, va_list ap)
|
|||||||
}
|
}
|
||||||
break;
|
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':
|
case 'n':
|
||||||
{
|
{
|
||||||
/* Output the number of characters written */
|
/* Output the number of characters written */
|
||||||
|
|||||||
Reference in New Issue
Block a user