diff --git a/lib/print.c b/lib/print.c index e9cb8fe..c250f33 100644 --- a/lib/print.c +++ b/lib/print.c @@ -22,6 +22,7 @@ #include // standard definitions #include // boolean types #include // variadic utilities +#include // 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_abspow(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='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) diff --git a/lib/print.h b/lib/print.h index 7466024..85e811b 100644 --- a/lib/print.h +++ b/lib/print.h @@ -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