diff --git a/sys/fmt/fmt.c b/sys/fmt/fmt.c index 346ba8f3f0fd7d218335eea82e6c7b4885fc6a09..ab2d594ad5b6bdfda7c440f9c8929774acd1c6a5 100644 --- a/sys/fmt/fmt.c +++ b/sys/fmt/fmt.c @@ -1,15 +1,20 @@ -/** +/* * Copyright (C) 2015 Kaspar Schleiser <kaspar@schleiser.de> * * This file is subject to the terms and conditions of the GNU Lesser * General Public License v2.1. See the file LICENSE in the top level * directory for more details. - * - * @ingroup sys_fmt + */ + +/** + * @ingroup sys_fmt * @{ + * * @file - * @brief String formatting library implementation - * @author Kaspar Schleiser <kaspar@schleiser.de> + * @brief String formatting library implementation + * + * @author Kaspar Schleiser <kaspar@schleiser.de> + * * @} */ @@ -34,6 +39,17 @@ static inline int _is_digit(char c) return (c >= '0' && c <= '9'); } +static inline unsigned pwr(unsigned val, unsigned exp) +{ + unsigned res = 1; + + for (unsigned i = 0; i < exp; i++) { + res *= val; + } + + return res; +} + size_t fmt_byte_hex(char *out, uint8_t byte) { if (out) { @@ -122,6 +138,56 @@ size_t fmt_s32_dec(char *out, int32_t val) return fmt_u32_dec(out, val) + negative; } +size_t fmt_s16_dec(char *out, int16_t val) +{ + return fmt_s32_dec(out, val); +} + +size_t fmt_s16_dfp(char *out, int16_t val, unsigned fp_digits) +{ + int16_t absolute, divider; + size_t pos = 0; + size_t div_len, len; + unsigned e; + char tmp[4]; + + if (fp_digits > 4) { + return 0; + } + if (fp_digits == 0) { + return fmt_s16_dec(out, val); + } + if (val < 0) { + if (out) { + out[pos++] = '-'; + } + val *= -1; + } + + e = pwr(10, fp_digits); + absolute = (val / (int)e); + divider = val - (absolute * e); + + pos += fmt_s16_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_s16_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; +} + uint32_t scn_u32_dec(const char *str, size_t n) { uint32_t res = 0; diff --git a/sys/include/fmt.h b/sys/include/fmt.h index b6ebb3828a61787da29f36cb600f03e25b02e8f1..02162504d9d033d6aeb18b8d3caad05b7e3bfcd3 100644 --- a/sys/include/fmt.h +++ b/sys/include/fmt.h @@ -7,14 +7,16 @@ */ /** - * @defgroup sys_fmt string formatting - * @ingroup sys - * @brief Provides simple string formatting functions + * @defgroup sys_fmt string formatting + * @ingroup sys + * @brief Provides simple string formatting functions * * @{ + * * @file - * @brief string formatting API - * @author Kaspar Schleiser <kaspar@schleiser.de> + * @brief String formatting API + * + * @author Kaspar Schleiser <kaspar@schleiser.de> */ #ifndef FMT_H_ @@ -127,6 +129,49 @@ size_t fmt_u16_dec(char *out, uint16_t val); */ size_t fmt_s32_dec(char *out, int32_t val); +/** + * @brief Convert a int16 value to decimal string. + * + * Will add a leading "-" if @p val is negative. + * + * If @p out is NULL, will only return the number of bytes that would have + * been written. + * + * @param[out] out Pointer to output buffer, or NULL + * @param[in] val Value to convert + * + * @return nr of characters written to (or needed in) @p out + */ +size_t fmt_s16_dec(char *out, int16_t val); + +/** + * @brief Convert 16-bit fixed point number to a decimal string + * + * The input for this function is a signed 16-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. + * + * The resulting string will always be patted with zeros after the decimal point. + * + * For example: if @p val is -3548 and @p fp_digits is 2, the resulting string + * will be "-35.48". For @p val := 12010 and @p fp_digits := 3 the result will + * be "12.010". + * + * Will add a leading "-" if @p val is negative. + * + * 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, MUST be <= 4 + * @param[in] fp_digits Number of digits after the decimal point + * + * @return Length of the resulting string + * @return 0 if @p fp_digits is > 4 + */ +size_t fmt_s16_dfp(char *out, int16_t val, unsigned fp_digits); + /** * @brief Count characters until '\0' (exclusive) in @p str *