print: add floating point numbers support

This commit is contained in:
King Kévin 2017-12-14 10:23:31 +01:00
parent f5355fe75a
commit 2ede3ef1b2
2 changed files with 120 additions and 12 deletions

View File

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

View File

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