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 <stdlib.h> // standard definitions
|
||||||
#include <stdbool.h> // boolean types
|
#include <stdbool.h> // boolean types
|
||||||
#include <stdarg.h> // variadic utilities
|
#include <stdarg.h> // variadic utilities
|
||||||
|
#include <math.h> // mathematics utilities to handle floating points
|
||||||
|
|
||||||
/* own libraries */
|
/* own libraries */
|
||||||
#include "print.h" // printing utilities
|
#include "print.h" // printing utilities
|
||||||
|
@ -83,10 +84,10 @@ static size_t print_string(char** str, size_t* size, const char* s)
|
||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** print unsigned number
|
/** print unsigned integer
|
||||||
* @param[out] str string to print unsigned number on (use NULL to print on user output)
|
* @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,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] padding number of 0's to pad
|
||||||
* @param[in] sign if sign should be printed
|
* @param[in] sign if sign should be printed
|
||||||
* @return number of characters 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
|
return length; // return number of characters printed
|
||||||
}
|
}
|
||||||
|
|
||||||
/** print signed number
|
/** print signed integer
|
||||||
* @param[out] str string to print signed number on (use NULL to print on user output)
|
* @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,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] padding number of 0's to pad
|
||||||
* @param[in] sign if sign should be printed
|
* @param[in] sign if sign should be printed
|
||||||
* @return number of characters 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
|
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)
|
/** print nibble (half-byte)
|
||||||
* @param[out] str string to print nibble on (use NULL to print on user output)
|
* @param[out] str string to print nibble on (use NULL to print on user output)
|
||||||
* @param[in,out] size size of string
|
* @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
|
size_t length = 0; // total number of characters printed
|
||||||
uint32_t padding = 0; // number of padding 0's
|
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
|
bool sign = false; // if sign needs to be printed
|
||||||
while (*format) { // go through format string
|
while (*format) { // go through format string
|
||||||
padding = 0; // reset padding
|
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;
|
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
|
// check format specifier
|
||||||
switch (*format) {
|
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
|
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));
|
print_printed(&length, print_unsigned(str, size, va_arg(va,uint32_t), padding, sign));
|
||||||
break;
|
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
|
case 'D': // for int64_t, long long
|
||||||
print_printed(&length, print_signed(str, size, va_arg(va,int64_t), padding, sign));
|
print_printed(&length, print_signed(str, size, va_arg(va,int64_t), padding, sign));
|
||||||
break;
|
break;
|
||||||
case 'c': // for char, unsigned char
|
case 'f':
|
||||||
print_printed(&length, print_char(str, size, (char)(va_arg(va,int)))); // needs casting because the returned value is promoted
|
print_printed(&length, print_float(str, size, va_arg(va,double), padding, fractional, sign));
|
||||||
break;
|
break;
|
||||||
case 'x': // for uint8_t, uint16_t, uint32_t downcase hexadecimal
|
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));
|
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
|
case 'B': // for uint64_t bits
|
||||||
print_printed(&length, print_bits(str, size, va_arg(va,uint64_t), padding, sign));
|
print_printed(&length, print_bits(str, size, va_arg(va,uint64_t), padding, sign));
|
||||||
break;
|
break;
|
||||||
case 's': // for strings
|
|
||||||
print_printed(&length, print_string(str, size, va_arg(va,char*)));
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
print_error |= PRINT_ERROR_UNSUPPORTED; // set error
|
print_error |= PRINT_ERROR_UNSUPPORTED; // set error
|
||||||
print_printed(&length, print_char(str, size, *format)); // print character (unknown format specifier)
|
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)
|
/** 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:
|
* format specifier supported are:
|
||||||
* - c for character
|
* - c for character
|
||||||
* - s for string
|
* - s for string
|
||||||
|
@ -21,6 +21,7 @@
|
||||||
* - U for uint64_t unsigned integer
|
* - U for uint64_t unsigned integer
|
||||||
* - d for up to int32_t signed integer
|
* - d for up to int32_t signed integer
|
||||||
* - D for int64_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 up to uint32_t lower case hexadecimal
|
||||||
* - X for uint64_t lower case hexadecimal
|
* - X for uint64_t lower case hexadecimal
|
||||||
* - h for up to uint32_t upper case hexadecimal
|
* - h for up to uint32_t upper case hexadecimal
|
||||||
|
|
Loading…
Reference in New Issue