diff --git a/src/ruc/format/builder.cpp b/src/ruc/format/builder.cpp index 6ae2954..38caf38 100644 --- a/src/ruc/format/builder.cpp +++ b/src/ruc/format/builder.cpp @@ -5,6 +5,7 @@ */ #include // min +#include // std::to_chars #include // size_t #include // uint8_t, uint16_t, uint32_t, uint64_t #include // setprecision @@ -178,15 +179,46 @@ void Builder::putI64(int64_t value, void Builder::putF64(double number, uint8_t precision) const { - precision = std::min(precision, static_cast(std::numeric_limits::digits10)); - - std::stringstream stream; - stream - << std::fixed << std::setprecision(precision) - << number - << std::defaultfloat << std::setprecision(6); - std::string string = stream.str(); - m_builder << string; + char buffer[512]; + auto conversion = std::to_chars(buffer, buffer + sizeof(buffer), number); + auto converted = std::string(buffer, conversion.ptr - buffer); + + size_t dot = converted.find('.'); + size_t length = dot + precision + 1; + + // There is no number behind the decimal point + if (dot == std::string::npos) { + if (precision > 0) { + converted += ".0"; + } + m_builder << converted; + return; + } + + // Only round if there are more numbers behind the decimal point than the precision, + // or the number that comes after the maximum precision is higher than 4 + if (converted.length() > length && converted[length] > '4') { + for (size_t i = length - 1; i >= 0 && i < converted.length(); --i) { + if (converted[i] == '.') { + continue; + } + if (converted[i] < '9') { // Overflow stops here + converted[i]++; + break; + } + converted[i] = '0'; + } + } + + // Cut off the characters after the requested precision + if (converted.length() > length) { + size_t last_included_number = converted.find_last_not_of("0", length - 1); + // If precision is zero, also cut off the '.', otherwise include the '0' after the '.' + size_t last_character_is_dot_offset = (converted[last_included_number] == '.') ? (precision > 0 ? 1 : -1) : 0; + converted = converted.substr(0, last_included_number + last_character_is_dot_offset + 1); + } + + m_builder << converted; } void Builder::putString(std::string_view string, char fill, Align align, size_t width) const