- %k added
- %f rounding fixed
This commit is contained in:
parent
7a12fe54cd
commit
6374ef283f
125
embformat.c
125
embformat.c
@ -6,6 +6,7 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <sys/_types.h>
|
||||||
|
|
||||||
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
|
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
|
||||||
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
|
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
|
||||||
@ -14,17 +15,31 @@
|
|||||||
|
|
||||||
// format length ((nothing),l)
|
// format length ((nothing),l)
|
||||||
typedef enum {
|
typedef enum {
|
||||||
LEN_NORMAL, LEN_LONG
|
LEN_NORMAL,
|
||||||
|
LEN_LONG
|
||||||
} FmtLength;
|
} FmtLength;
|
||||||
|
|
||||||
// format flags ((nothing),0)
|
// format flags ((nothing),0)
|
||||||
typedef enum {
|
typedef enum {
|
||||||
FLAG_NO = 0, FLAG_LEADING_ZEROS = 1, FLAG_LEADING_SPACES = 2, FLAG_PREPEND_PLUS_SIGN = 4
|
FLAG_NO = 0,
|
||||||
|
FLAG_LEADING_ZEROS = 1,
|
||||||
|
FLAG_LEADING_SPACES = 2,
|
||||||
|
FLAG_PREPEND_PLUS_SIGN = 4
|
||||||
} FmtFlags;
|
} FmtFlags;
|
||||||
|
|
||||||
// format type
|
// format type
|
||||||
typedef enum {
|
typedef enum {
|
||||||
UNKNOWN = -1, LITERAL_PERCENT, SIGNED_INTEGER, UNSIGNED_INTEGER, DOUBLE, DOUBLE_EXPONENTIAL, UNSIGNED_HEXADECIMAL_INT, UNSIGNED_HEXADECIMAL_INT_UPPERCASE, STRING, CHARACTER
|
UNKNOWN = -1,
|
||||||
|
LITERAL_PERCENT,
|
||||||
|
SIGNED_INTEGER,
|
||||||
|
UNSIGNED_INTEGER,
|
||||||
|
DOUBLE,
|
||||||
|
DOUBLE_EXPONENTIAL,
|
||||||
|
DOUBLE_TRUNCATE,
|
||||||
|
UNSIGNED_HEXADECIMAL_INT,
|
||||||
|
UNSIGNED_HEXADECIMAL_INT_UPPERCASE,
|
||||||
|
STRING,
|
||||||
|
CHARACTER
|
||||||
} FmtType;
|
} FmtType;
|
||||||
|
|
||||||
struct _FmtWord;
|
struct _FmtWord;
|
||||||
@ -84,6 +99,21 @@ static uint64_t power(uint64_t a, unsigned long int n) {
|
|||||||
return a;
|
return a;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static double powerf(double d, long int n) {
|
||||||
|
if (n == 0) {
|
||||||
|
return 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
d = (n < 0) ? (1 / d) : d;
|
||||||
|
n = (n < 0) ? -n : n;
|
||||||
|
|
||||||
|
double d_orig = d;
|
||||||
|
for (unsigned long int i = 0; i < n - 1; i++) {
|
||||||
|
d *= d_orig;
|
||||||
|
}
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
// round to closest base^1 value
|
// round to closest base^1 value
|
||||||
static uint64_t round_to_base(uint64_t a, uint64_t base) {
|
static uint64_t round_to_base(uint64_t a, uint64_t base) {
|
||||||
uint64_t mod = a % base;
|
uint64_t mod = a % base;
|
||||||
@ -176,15 +206,15 @@ static int print_number(uint64_t u, bool negative, FmtWord *fmt, char *outbuf, s
|
|||||||
// convert int to string
|
// convert int to string
|
||||||
while (gntpd > 0) {
|
while (gntpd > 0) {
|
||||||
uint64_t place_value = (u / gntpd); // integer division!
|
uint64_t place_value = (u / gntpd); // integer division!
|
||||||
u -= place_value * gntpd; // substract value corresponding to the place
|
u -= place_value * gntpd; // substract value corresponding to the place
|
||||||
|
|
||||||
if (fmt->type == UNSIGNED_HEXADECIMAL_INT_UPPERCASE && place_value > 9) {
|
if (fmt->type == UNSIGNED_HEXADECIMAL_INT_UPPERCASE && place_value > 9) {
|
||||||
place_value += 6;
|
place_value += 6;
|
||||||
}
|
}
|
||||||
*outstr = sNumChars[place_value]; // print place value string
|
*outstr = sNumChars[place_value]; // print place value string
|
||||||
|
|
||||||
gntpd /= base; // divide by the base (advance to lower place)
|
gntpd /= base; // divide by the base (advance to lower place)
|
||||||
outstr++; // advance in the string
|
outstr++; // advance in the string
|
||||||
*outstr = '\0'; // move the terminating zero
|
*outstr = '\0'; // move the terminating zero
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193,10 +223,10 @@ static int print_number(uint64_t u, bool negative, FmtWord *fmt, char *outbuf, s
|
|||||||
int pad_n = MAX(fmt->width - digits - sign_prepended, 0);
|
int pad_n = MAX(fmt->width - digits - sign_prepended, 0);
|
||||||
if (pad_n > 0) {
|
if (pad_n > 0) {
|
||||||
// pad differently based on padding character
|
// pad differently based on padding character
|
||||||
if (fmt->flags & FLAG_LEADING_ZEROS) { // -00000nnn
|
if (fmt->flags & FLAG_LEADING_ZEROS) { // -00000nnn
|
||||||
insert_leading_characters(outstrbuf + sign_prepended, INT_PRINT_OUTBUF_SIZE - sign_prepended, '0', pad_n); // insert leading characters
|
insert_leading_characters(outstrbuf + sign_prepended, INT_PRINT_OUTBUF_SIZE - sign_prepended, '0', pad_n); // insert leading characters
|
||||||
} else if (fmt->flags & FLAG_LEADING_SPACES) { // _____-nnn
|
} else if (fmt->flags & FLAG_LEADING_SPACES) { // _____-nnn
|
||||||
insert_leading_characters(outstrbuf, INT_PRINT_OUTBUF_SIZE, ' ', pad_n); // insert leading characters
|
insert_leading_characters(outstrbuf, INT_PRINT_OUTBUF_SIZE, ' ', pad_n); // insert leading characters
|
||||||
}
|
}
|
||||||
outstr += pad_n; // advance pointer to the end of the string
|
outstr += pad_n; // advance pointer to the end of the string
|
||||||
}
|
}
|
||||||
@ -226,7 +256,7 @@ static int pfn_integer(va_list *va, FmtWord *fmt, char *outbuf, size_t free_spac
|
|||||||
u = si;
|
u = si;
|
||||||
}
|
}
|
||||||
} else if (fmt->type == UNSIGNED_INTEGER || fmt->type == UNSIGNED_HEXADECIMAL_INT || fmt->type == UNSIGNED_HEXADECIMAL_INT_UPPERCASE) { // for UNsigned integers
|
} else if (fmt->type == UNSIGNED_INTEGER || fmt->type == UNSIGNED_HEXADECIMAL_INT || fmt->type == UNSIGNED_HEXADECIMAL_INT_UPPERCASE) { // for UNsigned integers
|
||||||
if (fmt->length == LEN_NORMAL) { // ...without length specifiers
|
if (fmt->length == LEN_NORMAL) { // ...without length specifiers
|
||||||
unsigned int d = va_arg((*va), unsigned int);
|
unsigned int d = va_arg((*va), unsigned int);
|
||||||
u = d;
|
u = d;
|
||||||
} else { // ...with length specifiers
|
} else { // ...with length specifiers
|
||||||
@ -249,11 +279,17 @@ static int pfn_double(va_list *va, FmtWord *fmt, char *outbuf, size_t free_space
|
|||||||
d *= -1;
|
d *= -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// round to requested precision if trucation is not instructed
|
||||||
|
if (fmt->type != DOUBLE_TRUNCATE) {
|
||||||
|
double r = 0.5 * powerf(10, -fmt->precision);
|
||||||
|
d += r;
|
||||||
|
}
|
||||||
|
|
||||||
// normalize if exponential form is required
|
// normalize if exponential form is required
|
||||||
int exponent = 0;
|
int exponent = 0;
|
||||||
if (fmt->type == DOUBLE_EXPONENTIAL) {
|
if (fmt->type == DOUBLE_EXPONENTIAL) {
|
||||||
double q = d < 1.0 ? 10.0 : 0.1; // quotient
|
double q = d < 1.0 ? 10.0 : 0.1; // quotient
|
||||||
int d_exp = d < 1.0 ? -1 : 1; // shift per iteration in exponent
|
int d_exp = d < 1.0 ? -1 : 1; // shift per iteration in exponent
|
||||||
while (!(d >= 1.0 && d < 10.0)) {
|
while (!(d >= 1.0 && d < 10.0)) {
|
||||||
d *= q;
|
d *= q;
|
||||||
exponent += d_exp;
|
exponent += d_exp;
|
||||||
@ -264,7 +300,7 @@ static int pfn_double(va_list *va, FmtWord *fmt, char *outbuf, size_t free_space
|
|||||||
FmtWord int_fmt = {.flags = fmt->flags, .type = UNSIGNED_INTEGER, .width = fmt->width};
|
FmtWord int_fmt = {.flags = fmt->flags, .type = UNSIGNED_INTEGER, .width = fmt->width};
|
||||||
|
|
||||||
// separate into integer and fractional part
|
// separate into integer and fractional part
|
||||||
uint64_t int_part = (uint64_t) d; // integer part
|
uint64_t int_part = (uint64_t)d; // integer part
|
||||||
int copy_len = print_number(int_part, negative, &int_fmt, outbuf, free_space);
|
int copy_len = print_number(int_part, negative, &int_fmt, outbuf, free_space);
|
||||||
free_space -= copy_len;
|
free_space -= copy_len;
|
||||||
outbuf += copy_len;
|
outbuf += copy_len;
|
||||||
@ -285,7 +321,7 @@ static int pfn_double(va_list *va, FmtWord *fmt, char *outbuf, size_t free_space
|
|||||||
FmtWord frac_fmt = {.flags = FLAG_NO, .width = -1, .type = UNSIGNED_INTEGER};
|
FmtWord frac_fmt = {.flags = FLAG_NO, .width = -1, .type = UNSIGNED_INTEGER};
|
||||||
|
|
||||||
// get "leading zeros" in fractional part
|
// get "leading zeros" in fractional part
|
||||||
double d_frac = d - (double) int_part;
|
double d_frac = d - (double)int_part;
|
||||||
|
|
||||||
// print leading zeros
|
// print leading zeros
|
||||||
int leading_zeros_printed = 0;
|
int leading_zeros_printed = 0;
|
||||||
@ -300,9 +336,8 @@ static int pfn_double(va_list *va, FmtWord *fmt, char *outbuf, size_t free_space
|
|||||||
}
|
}
|
||||||
|
|
||||||
// extract fractional part as integer
|
// extract fractional part as integer
|
||||||
d_frac *= (double) power(10, fmt->precision - leading_zeros_printed + 1); // get one more digit
|
d_frac *= (double)power(10, fmt->precision - leading_zeros_printed);
|
||||||
uint64_t frac_part = (uint64_t) d_frac;
|
uint64_t frac_part = (uint64_t)d_frac;
|
||||||
frac_part = round_to_base(frac_part, 10) / 10; // remove last zero digit (result of rounding)
|
|
||||||
|
|
||||||
// print fractional part
|
// print fractional part
|
||||||
copy_len = print_number(frac_part, false, &frac_fmt, outbuf, free_space);
|
copy_len = print_number(frac_part, false, &frac_fmt, outbuf, free_space);
|
||||||
@ -348,23 +383,25 @@ static int pfn_char(va_list *va, FmtWord *fmt, char *outbuf, size_t free_space)
|
|||||||
|
|
||||||
// print string
|
// print string
|
||||||
static int pfn_string(va_list *va, FmtWord *fmt, char *outbuf, size_t free_space) {
|
static int pfn_string(va_list *va, FmtWord *fmt, char *outbuf, size_t free_space) {
|
||||||
char *str = va_arg((*va), char*);
|
char *str = va_arg((*va), char *);
|
||||||
return string_copy(outbuf, str, free_space);
|
return string_copy(outbuf, str, free_space);
|
||||||
}
|
}
|
||||||
|
|
||||||
// format swting assignment table
|
// format swting assignment table
|
||||||
static FmtTypeDesignatorPair sTypeDesAssignment[] = {{'%', LITERAL_PERCENT, pfn_literal_percent},
|
static FmtTypeDesignatorPair sTypeDesAssignment[] = {
|
||||||
{'d', SIGNED_INTEGER, pfn_integer},
|
{'%', LITERAL_PERCENT, pfn_literal_percent},
|
||||||
{'i', SIGNED_INTEGER, pfn_integer},
|
{'d', SIGNED_INTEGER, pfn_integer},
|
||||||
{'u', UNSIGNED_INTEGER, pfn_integer},
|
{'i', SIGNED_INTEGER, pfn_integer},
|
||||||
{'f', DOUBLE, pfn_double},
|
{'u', UNSIGNED_INTEGER, pfn_integer},
|
||||||
{'e', DOUBLE_EXPONENTIAL, pfn_double},
|
{'f', DOUBLE, pfn_double},
|
||||||
{'x', UNSIGNED_HEXADECIMAL_INT, pfn_integer},
|
{'e', DOUBLE_EXPONENTIAL, pfn_double},
|
||||||
{'X', UNSIGNED_HEXADECIMAL_INT_UPPERCASE, pfn_integer},
|
{'k', DOUBLE_TRUNCATE, pfn_double},
|
||||||
{'p', UNSIGNED_HEXADECIMAL_INT, pfn_integer},
|
{'x', UNSIGNED_HEXADECIMAL_INT, pfn_integer},
|
||||||
{'s', STRING, pfn_string},
|
{'X', UNSIGNED_HEXADECIMAL_INT_UPPERCASE, pfn_integer},
|
||||||
{'c', CHARACTER, pfn_char},
|
{'p', UNSIGNED_HEXADECIMAL_INT, pfn_integer},
|
||||||
{'\0', UNKNOWN, NULL} // termination
|
{'s', STRING, pfn_string},
|
||||||
|
{'c', CHARACTER, pfn_char},
|
||||||
|
{'\0', UNKNOWN, NULL} // termination
|
||||||
};
|
};
|
||||||
|
|
||||||
// ------------------------------------------------------------
|
// ------------------------------------------------------------
|
||||||
@ -427,9 +464,9 @@ static const char *seek_delimiter(const char *str) {
|
|||||||
// return: pointer to unprocessed input string
|
// return: pointer to unprocessed input string
|
||||||
static const char *locate_format_word(const char *str, const char **begin, size_t *length) {
|
static const char *locate_format_word(const char *str, const char **begin, size_t *length) {
|
||||||
const char *delim_pos = seek_delimiter(str); // seek for format specifier begin
|
const char *delim_pos = seek_delimiter(str); // seek for format specifier begin
|
||||||
if (delim_pos == NULL) { // if not found...
|
if (delim_pos == NULL) { // if not found...
|
||||||
*length = 0; // set to zero if not found
|
*length = 0; // set to zero if not found
|
||||||
return NULL; // ...then return
|
return NULL; // ...then return
|
||||||
}
|
}
|
||||||
|
|
||||||
// here we have pointer to the '%' character of a format "word"
|
// here we have pointer to the '%' character of a format "word"
|
||||||
@ -506,17 +543,17 @@ static int process_format_word(char *str, FmtWord *word, int *rewind) {
|
|||||||
word->flags = FLAG_NO;
|
word->flags = FLAG_NO;
|
||||||
while (is_flag(*str)) {
|
while (is_flag(*str)) {
|
||||||
switch (*str) {
|
switch (*str) {
|
||||||
case '0':
|
case '0':
|
||||||
word->flags |= FLAG_LEADING_ZEROS;
|
word->flags |= FLAG_LEADING_ZEROS;
|
||||||
break;
|
break;
|
||||||
case ' ':
|
case ' ':
|
||||||
word->flags |= FLAG_LEADING_SPACES;
|
word->flags |= FLAG_LEADING_SPACES;
|
||||||
break;
|
break;
|
||||||
case '+':
|
case '+':
|
||||||
word->flags |= FLAG_PREPEND_PLUS_SIGN;
|
word->flags |= FLAG_PREPEND_PLUS_SIGN;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
str++;
|
str++;
|
||||||
}
|
}
|
||||||
@ -575,7 +612,7 @@ unsigned long int vembfmt(char *str, unsigned long int len, const char *format,
|
|||||||
sum_copy_len += copy_len;
|
sum_copy_len += copy_len;
|
||||||
|
|
||||||
// print data
|
// print data
|
||||||
FmtWord word = { 0 };
|
FmtWord word = {0};
|
||||||
int rewind;
|
int rewind;
|
||||||
process_format_word(word_str, &word, &rewind);
|
process_format_word(word_str, &word, &rewind);
|
||||||
if (word.type != UNKNOWN) {
|
if (word.type != UNKNOWN) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user