diff --git a/sys/fmt/fmt.c b/sys/fmt/fmt.c index 62e69cfc6a..fa0de4026e 100644 --- a/sys/fmt/fmt.c +++ b/sys/fmt/fmt.c @@ -35,6 +35,17 @@ ssize_t write(int fildes, const void *buf, size_t nbyte); static const char _hex_chars[16] = "0123456789ABCDEF"; +static const uint32_t _tenmap[] = { + 0, + 10LU, + 100LU, + 1000LU, + 10000LU, + 100000LU, + 1000000LU, + 10000000LU, +}; + static inline int _is_digit(char c) { return (c >= '0' && c <= '9'); @@ -243,16 +254,48 @@ size_t fmt_s16_dfp(char *out, int16_t val, unsigned fp_digits) return pos; } -static const uint32_t _tenmap[] = { - 0, - 10LU, - 100LU, - 1000LU, - 10000LU, - 100000LU, - 1000000LU, - 10000000LU, -}; +size_t fmt_s32_dfp(char *out, int32_t val, unsigned fp_digits) +{ + int32_t absolute, divider; + unsigned div_len, len, pos = 0; + char tmp[9]; + + if (fp_digits > 9) { + return 0; + } + if (fp_digits == 0) { + return fmt_s32_dec(out, val); + } + if (val < 0) { + if (out) { + out[pos++] = '-'; + } + val = -val; + } + + uint32_t e = _tenmap[fp_digits]; + absolute = (val / e); + divider = val - (absolute * e); + + pos += fmt_s32_dec(&out[pos], absolute); + + if (!out) { + return pos + 1 + fp_digits; /* abs len + decimal point + divider */ + } + + out[pos++] = '.'; + len = pos + fp_digits; + div_len = fmt_s32_dec(tmp, divider); + + while (pos < (len - div_len)) { + out[pos++] = '0'; + } + for (size_t i = 0; i < div_len; i++) { + out[pos++] = tmp[i]; + } + + return pos; +} /* this is very probably not the most efficient implementation, as it at least * pulls in floating point math. But it works, and it's always nice to have diff --git a/sys/include/fmt.h b/sys/include/fmt.h index 01f0b6678b..afdb5e2d95 100644 --- a/sys/include/fmt.h +++ b/sys/include/fmt.h @@ -203,6 +203,34 @@ size_t fmt_s16_dec(char *out, int16_t val); */ size_t fmt_s16_dfp(char *out, int16_t val, unsigned fp_digits); +/** + * @brief Convert 32-bit fixed point number to a decimal string + * + * The input for this function is a signed 32-bit integer holding the fixed + * point value as well as an unsigned integer defining the position of the + * decimal point, so this value defines the number of decimal digits after the + * decimal point. + * + * Will add a leading "-" if @p val is negative. + * + * The resulting string will always be patted with zeros after the decimal point. + * + * For example: if @p val is -314159 and @p fp_digits is 5, the resulting string + * will be "-3.14159". For @p val := 16777215 and @p fp_digits := 6 the result + * will be "16.777215". + * + * If @p out is NULL, will only return the number of bytes that would have + * been written. + * + * @param[out] out Pointer to the output buffer, or NULL + * @param[in] val Fixed point value + * @param[in] fp_digits Number of digits after the decimal point, MUST be <= 9 + * + * @return Length of the resulting string + * @return 0 if @p fp_digits is > 9 + */ +size_t fmt_s32_dfp(char *out, int32_t val, unsigned fp_digits); + /** * @brief Format float to string *