- %k added

- %f rounding fixed
This commit is contained in:
Wiesner András 2024-11-05 13:22:45 +01:00
parent 7a12fe54cd
commit 6374ef283f

View File

@ -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) {