print: add floating point numbers support
This commit is contained in:
parent
f5355fe75a
commit
2ede3ef1b2
129
lib/print.c
129
lib/print.c
|
@ -22,6 +22,7 @@
|
|||
#include <stdlib.h> // standard definitions
|
||||
#include <stdbool.h> // boolean types
|
||||
#include <stdarg.h> // variadic utilities
|
||||
#include <math.h> // mathematics utilities to handle floating points
|
||||
|
||||
/* own libraries */
|
||||
#include "print.h" // printing utilities
|
||||
|
@ -83,10 +84,10 @@ static size_t print_string(char** str, size_t* size, const char* s)
|
|||
return length;
|
||||
}
|
||||
|
||||
/** print unsigned number
|
||||
* @param[out] str string to print unsigned number on (use NULL to print on user output)
|
||||
/** print unsigned integer
|
||||
* @param[out] str string to print unsigned integer on (use NULL to print on user output)
|
||||
* @param[in,out] size size of string
|
||||
* @param[in] u unsigned number to be printed
|
||||
* @param[in] u unsigned integer to be printed
|
||||
* @param[in] padding number of 0's to pad
|
||||
* @param[in] sign if sign should be printed
|
||||
* @return number of characters printed
|
||||
|
@ -111,10 +112,10 @@ static size_t print_unsigned(char** str, size_t* size, uint64_t u, uint32_t padd
|
|||
return length; // return number of characters printed
|
||||
}
|
||||
|
||||
/** print signed number
|
||||
* @param[out] str string to print signed number on (use NULL to print on user output)
|
||||
/** print signed integer
|
||||
* @param[out] str string to print signed integer on (use NULL to print on user output)
|
||||
* @param[in,out] size size of string
|
||||
* @param[in] d signed number to be printed
|
||||
* @param[in] d signed integer to be printed
|
||||
* @param[in] padding number of 0's to pad
|
||||
* @param[in] sign if sign should be printed
|
||||
* @return number of characters printed
|
||||
|
@ -130,6 +131,85 @@ static size_t print_signed(char** str, size_t* size, int64_t d, uint32_t padding
|
|||
return length; // return number of characters printed
|
||||
}
|
||||
|
||||
/** print floating number
|
||||
* @param[out] str string to print floating number on (use NULL to print on user output)
|
||||
* @param[in,out] size size of string
|
||||
* @param[in] f floating number to be printed
|
||||
* @param[in] padding number of 0's to pad
|
||||
* @param[in] sign if sign should be printed
|
||||
* @return number of characters printed
|
||||
**/
|
||||
static size_t print_float(char** str, size_t* size, double f, uint32_t padding, uint32_t fractional, bool sign) {
|
||||
size_t length = 0; // number of characters printed
|
||||
if (isnan(f)) { // not a number
|
||||
print_printed(&length, print_string(str, size, "NaN")); // print NaN
|
||||
} else if (isinf(f)) { // infinite
|
||||
if (-1==isinf(f)) {
|
||||
print_printed(&length, print_char(str, size, '-')); // print sign
|
||||
} else if (sign) {
|
||||
print_printed(&length, print_char(str, size, '+')); // print sign
|
||||
}
|
||||
print_printed(&length, print_string(str, size, "inf")); // print inf
|
||||
} else if (isnormal(f)) { // it should be not 0
|
||||
if (f<0) {
|
||||
print_printed(&length, print_char(str, size, '-')); // print sign
|
||||
} else if (sign) {
|
||||
print_printed(&length, print_char(str, size, '+')); // print sign
|
||||
}
|
||||
double f_abs = fabs(f); // only work using the absolute value now that the sign is printed
|
||||
// get the exponent
|
||||
int8_t exponent = 0; // exponent min/max for double is 37
|
||||
if (f_abs<1.0) {
|
||||
while (f_abs<pow(10.0, exponent-1)) { // find negative exponent, base 10
|
||||
exponent -= 1; // decrement in deci
|
||||
}
|
||||
if (padding) { // respect padding wish
|
||||
exponent -= padding;
|
||||
}
|
||||
} else {
|
||||
while (f_abs>pow(10.0, exponent)) { // find the positive exponent, base 10
|
||||
exponent += 3; // increment in kilo
|
||||
}
|
||||
if (padding) { // respect padding wish
|
||||
exponent -= padding;
|
||||
} else {
|
||||
exponent -= 3;
|
||||
}
|
||||
}
|
||||
// print integer part
|
||||
f_abs /= pow(10.0, exponent); // convert to scientific format
|
||||
print_printed(&length, print_unsigned(str, size, f_abs, padding, false)); // print integer part as scientific number
|
||||
// print fractional part
|
||||
if (fractional) {
|
||||
print_printed(&length, print_char(str, size, '.')); // print decimal point
|
||||
f_abs -= (uint64_t)f_abs; // remove integer part
|
||||
for (uint32_t frac=0; frac<fractional; frac++) { // print fractional parts
|
||||
f_abs *= 10.0;
|
||||
print_printed(&length, print_unsigned(str, size, f_abs, 0, false));
|
||||
f_abs -= (uint64_t)f_abs;
|
||||
}
|
||||
}
|
||||
// print exponent
|
||||
if (exponent) {
|
||||
print_printed(&length, print_char(str, size, 'E')); // print exponent mark
|
||||
print_printed(&length, print_signed(str, size, exponent, 0, false));
|
||||
}
|
||||
} else { // f=0
|
||||
// print sign
|
||||
if (f<0) {
|
||||
print_printed(&length, print_char(str, size, '-')); // print sign
|
||||
} else if (sign) {
|
||||
print_printed(&length, print_char(str, size, '+')); // print sign
|
||||
}
|
||||
print_printed(&length, print_unsigned(str, size, 0, padding, false)); // print integer part
|
||||
if (fractional) {
|
||||
print_printed(&length, print_char(str, size, '.')); // print decimal point
|
||||
print_printed(&length, print_unsigned(str, size, 0, fractional, false)); // print fractional part
|
||||
}
|
||||
}
|
||||
return length; // return number of characters printed
|
||||
}
|
||||
|
||||
/** print nibble (half-byte)
|
||||
* @param[out] str string to print nibble on (use NULL to print on user output)
|
||||
* @param[in,out] size size of string
|
||||
|
@ -236,6 +316,7 @@ static size_t vsnprintf(char** str, size_t* size, const char *format, va_list va
|
|||
{
|
||||
size_t length = 0; // total number of characters printed
|
||||
uint32_t padding = 0; // number of padding 0's
|
||||
uint32_t fractional = 0; // number or fractional digits for floating point numbers
|
||||
bool sign = false; // if sign needs to be printed
|
||||
while (*format) { // go through format string
|
||||
padding = 0; // reset padding
|
||||
|
@ -278,8 +359,37 @@ static size_t vsnprintf(char** str, size_t* size, const char *format, va_list va
|
|||
goto end;
|
||||
}
|
||||
}
|
||||
// check fractional
|
||||
if ('.'==*format) { // fractional required
|
||||
format++; // go to fractional number
|
||||
while (*format>='0' && *format<='9') {
|
||||
if (fractional>UINT32_MAX/10) { // check for overflow
|
||||
print_error |= PRINT_ERROR_UNSUPPORTED; // set error
|
||||
goto end;
|
||||
}
|
||||
fractional *= 10; // go to next magnitude
|
||||
if (fractional>UINT32_MAX-(*format-'0')) { // check for overflow
|
||||
print_error |= PRINT_ERROR_UNSUPPORTED; // set error
|
||||
goto end;
|
||||
}
|
||||
fractional += *format-'0'; // save digit
|
||||
format++; // go to next character
|
||||
}
|
||||
if (0==*format) { // end of string detected
|
||||
print_error |= PRINT_ERROR_MALFORMED; // set error
|
||||
goto end;
|
||||
}
|
||||
} else {
|
||||
fractional = 2; // default fractional precision
|
||||
}
|
||||
// check format specifier
|
||||
switch (*format) {
|
||||
case 'c': // for char, unsigned char
|
||||
print_printed(&length, print_char(str, size, (char)(va_arg(va,int)))); // needs casting because the returned value is promoted
|
||||
break;
|
||||
case 's': // for strings
|
||||
print_printed(&length, print_string(str, size, va_arg(va,char*)));
|
||||
break;
|
||||
case 'u': // for uint8_t, uint16_t, uint32_t, unsigned int, unsigned long
|
||||
print_printed(&length, print_unsigned(str, size, va_arg(va,uint32_t), padding, sign));
|
||||
break;
|
||||
|
@ -292,8 +402,8 @@ static size_t vsnprintf(char** str, size_t* size, const char *format, va_list va
|
|||
case 'D': // for int64_t, long long
|
||||
print_printed(&length, print_signed(str, size, va_arg(va,int64_t), padding, sign));
|
||||
break;
|
||||
case 'c': // for char, unsigned char
|
||||
print_printed(&length, print_char(str, size, (char)(va_arg(va,int)))); // needs casting because the returned value is promoted
|
||||
case 'f':
|
||||
print_printed(&length, print_float(str, size, va_arg(va,double), padding, fractional, sign));
|
||||
break;
|
||||
case 'x': // for uint8_t, uint16_t, uint32_t downcase hexadecimal
|
||||
print_printed(&length, print_hex(str, size, va_arg(va,uint32_t), padding, sign, false));
|
||||
|
@ -313,9 +423,6 @@ static size_t vsnprintf(char** str, size_t* size, const char *format, va_list va
|
|||
case 'B': // for uint64_t bits
|
||||
print_printed(&length, print_bits(str, size, va_arg(va,uint64_t), padding, sign));
|
||||
break;
|
||||
case 's': // for strings
|
||||
print_printed(&length, print_string(str, size, va_arg(va,char*)));
|
||||
break;
|
||||
default:
|
||||
print_error |= PRINT_ERROR_UNSUPPORTED; // set error
|
||||
print_printed(&length, print_char(str, size, *format)); // print character (unknown format specifier)
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
*
|
||||
*/
|
||||
/** printing utilities to replace the large printf from the standard library (API)
|
||||
* @note use % as format specifier prefix, followed by + to enforce sign or 0x prefix, 0 followed by n for padding, . followed by n for number for fractional, and format specifier
|
||||
* @note use % as format specifier prefix, followed by + to enforce sign or 0x prefix, 0 followed by n for padding or forcing integer part of floating point number, . followed by n for number for fractional precision of floating point numbers, and format specifier
|
||||
* format specifier supported are:
|
||||
* - c for character
|
||||
* - s for string
|
||||
|
@ -21,6 +21,7 @@
|
|||
* - U for uint64_t unsigned integer
|
||||
* - d for up to int32_t signed integer
|
||||
* - D for int64_t signed integer
|
||||
* - f for float and double floating point numbers
|
||||
* - x for up to uint32_t lower case hexadecimal
|
||||
* - X for uint64_t lower case hexadecimal
|
||||
* - h for up to uint32_t upper case hexadecimal
|
||||
|
|
Loading…
Reference in New Issue