diff --git a/sys/fmt/fmt.c b/sys/fmt/fmt.c index 9c951be387a18f2b518f1d56f3a01de01da7e131..e75c7487a34d36c1a27879e7dc19f944a0f0ff23 100644 --- a/sys/fmt/fmt.c +++ b/sys/fmt/fmt.c @@ -263,48 +263,56 @@ 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) +size_t fmt_s16_dfp(char *out, int16_t val, int fp_digits) { return fmt_s32_dfp(out, val, fp_digits); } -size_t fmt_s32_dfp(char *out, int32_t val, unsigned fp_digits) +size_t fmt_s32_dfp(char *out, int32_t val, int fp_digits) { - assert(fp_digits < TENMAP_SIZE); + assert(fp_digits > -(int)TENMAP_SIZE); - int32_t absolute, divider; - unsigned div_len, len, pos = 0; - char tmp[9]; + unsigned pos = 0; if (fp_digits == 0) { - return fmt_s32_dec(out, val); + pos = fmt_s32_dec(out, val); } - if (val < 0) { + else if (fp_digits > 0) { + pos = fmt_s32_dec(out, val); if (out) { - out[pos++] = '-'; + memset(&out[pos], '0', fp_digits); } - 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 */ + pos += fp_digits; } + else { + fp_digits *= -1; + uint32_t e = _tenmap[fp_digits]; + int32_t abs = (val / (int32_t)e); + int32_t div = val - (abs * e); + + /* the divisor should never be negative */ + if (div < 0) { + div *= -1; + } + /* handle special case for negative number with zero as absolute value */ + if ((abs == 0) && (val < 0)) { + if (out) { + out[pos] = '-'; + } + pos++; + } - 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]; + if (!out) { + /* compensate for the decimal point character... */ + pos += fmt_s32_dec(NULL, abs) + 1; + } + else { + pos += fmt_s32_dec(&out[pos], abs); + out[pos++] = '.'; + unsigned div_len = fmt_s32_dec(&out[pos], div); + fmt_lpad(&out[pos], div_len, (size_t)fp_digits, '0'); + } + pos += fp_digits; } return pos; diff --git a/sys/include/fmt.h b/sys/include/fmt.h index 3f9e1ea00e829bac43da76c0d5dc58de09229bbb..d88608b2c9266593fbddb0236c781b725ee14d04 100644 --- a/sys/include/fmt.h +++ b/sys/include/fmt.h @@ -225,60 +225,43 @@ 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. - * - * @pre fp_digits < 8 (TENMAP_SIZE) + * See fmt_s32_dfp() for more details * * @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 + * @param[in] fp_digits Number of digits after the decimal point, MUST be + * >= -7 * * @return Length of the resulting string */ -size_t fmt_s16_dfp(char *out, int16_t val, unsigned fp_digits); +size_t fmt_s16_dfp(char *out, int16_t val, int 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. + * point value as well as an integer defining the position of the decimal point. + * This value is used to shift the decimal point to the right (positive value + * of @p fp_digits) or to the left (negative value of @p fp_digits). * * 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. + * For example: if @p val is -3548 and @p fp_digits is -2, the resulting string + * will be "-35.48". The same value for @p val with @p fp_digits of 2 will + * result in "-354800". * - * @pre fp_digits < 8 (TENMAP_SIZE) + * @pre fp_digits > -8 (TENMAP_SIZE) * * @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 + * @param[in] fp_digits Number of digits after the decimal point, MUST be + * >= -7 * * @return Length of the resulting string */ -size_t fmt_s32_dfp(char *out, int32_t val, unsigned fp_digits); +size_t fmt_s32_dfp(char *out, int32_t val, int fp_digits); /** * @brief Format float to string