Format: Improve the double formatting implementation
This commit is contained in:
@@ -5,6 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <algorithm> // min
|
#include <algorithm> // min
|
||||||
|
#include <charconv> // std::to_chars
|
||||||
#include <cstddef> // size_t
|
#include <cstddef> // size_t
|
||||||
#include <cstdint> // uint8_t, uint16_t, uint32_t, uint64_t
|
#include <cstdint> // uint8_t, uint16_t, uint32_t, uint64_t
|
||||||
#include <iomanip> // setprecision
|
#include <iomanip> // setprecision
|
||||||
@@ -178,15 +179,46 @@ void Builder::putI64(int64_t value,
|
|||||||
|
|
||||||
void Builder::putF64(double number, uint8_t precision) const
|
void Builder::putF64(double number, uint8_t precision) const
|
||||||
{
|
{
|
||||||
precision = std::min(precision, static_cast<uint8_t>(std::numeric_limits<double>::digits10));
|
char buffer[512];
|
||||||
|
auto conversion = std::to_chars(buffer, buffer + sizeof(buffer), number);
|
||||||
|
auto converted = std::string(buffer, conversion.ptr - buffer);
|
||||||
|
|
||||||
std::stringstream stream;
|
size_t dot = converted.find('.');
|
||||||
stream
|
size_t length = dot + precision + 1;
|
||||||
<< std::fixed << std::setprecision(precision)
|
|
||||||
<< number
|
// There is no number behind the decimal point
|
||||||
<< std::defaultfloat << std::setprecision(6);
|
if (dot == std::string::npos) {
|
||||||
std::string string = stream.str();
|
if (precision > 0) {
|
||||||
m_builder << string;
|
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
|
void Builder::putString(std::string_view string, char fill, Align align, size_t width) const
|
||||||
|
|||||||
Reference in New Issue
Block a user