AK: Support long doubles in format strings

This commit is contained in:
Hediadyoin1 2021-05-07 11:32:01 +02:00 committed by Andreas Kling
parent f4072a5038
commit 8ed3315dec
2 changed files with 91 additions and 0 deletions

View file

@ -380,6 +380,53 @@ void FormatBuilder::put_f64(
put_string(string_builder.string_view(), align, min_width, NumericLimits<size_t>::max(), fill);
}
void FormatBuilder::put_f80(
long double value,
u8 base,
bool upper_case,
Align align,
size_t min_width,
size_t precision,
char fill,
SignMode sign_mode)
{
StringBuilder string_builder;
FormatBuilder format_builder { string_builder };
bool is_negative = value < 0.0;
if (is_negative)
value = -value;
format_builder.put_u64(static_cast<u64>(value), base, false, upper_case, false, Align::Right, 0, ' ', sign_mode, is_negative);
if (precision > 0) {
// FIXME: This is a terrible approximation but doing it properly would be a lot of work. If someone is up for that, a good
// place to start would be the following video from CppCon 2019:
// https://youtu.be/4P_kbF0EbZM (Stephan T. Lavavej “Floating-Point <charconv>: Making Your Code 10x Faster With C++17's Final Boss”)
value -= static_cast<i64>(value);
long double epsilon = 0.5;
for (size_t i = 0; i < precision; ++i)
epsilon /= 10.0;
size_t visible_precision = 0;
for (; visible_precision < precision; ++visible_precision) {
if (value - static_cast<i64>(value) < epsilon)
break;
value *= 10.0;
epsilon *= 10.0;
}
if (visible_precision > 0) {
string_builder.append('.');
format_builder.put_u64(static_cast<u64>(value), base, false, upper_case, true, Align::Right, visible_precision);
}
}
put_string(string_builder.string_view(), align, min_width, NumericLimits<size_t>::max(), fill);
}
#endif
void FormatBuilder::put_hexdump(ReadonlyBytes bytes, size_t width, char fill)
@ -608,6 +655,29 @@ void Formatter<bool>::format(FormatBuilder& builder, bool value)
}
}
#ifndef KERNEL
void Formatter<long double>::format(FormatBuilder& builder, long double value)
{
u8 base;
bool upper_case;
if (m_mode == Mode::Default || m_mode == Mode::Float) {
base = 10;
upper_case = false;
} else if (m_mode == Mode::Hexfloat) {
base = 16;
upper_case = false;
} else if (m_mode == Mode::HexfloatUppercase) {
base = 16;
upper_case = true;
} else {
VERIFY_NOT_REACHED();
}
m_width = m_width.value_or(0);
m_precision = m_precision.value_or(6);
builder.put_f80(value, base, upper_case, m_align, m_width.value(), m_precision.value(), m_fill, m_sign_mode);
}
void Formatter<double>::format(FormatBuilder& builder, double value)
{
u8 base;

View file

@ -185,6 +185,16 @@ public:
SignMode sign_mode = SignMode::OnlyIfNeeded);
#ifndef KERNEL
void put_f80(
long double value,
u8 base = 10,
bool upper_case = false,
Align align = Align::Right,
size_t min_width = 0,
size_t precision = 6,
char fill = ' ',
SignMode sign_mode = SignMode::OnlyIfNeeded);
void put_f64(
double value,
u8 base = 10,
@ -397,6 +407,17 @@ struct Formatter<double> : StandardFormatter {
void format(FormatBuilder&, double value);
};
template<>
struct Formatter<long double> : StandardFormatter {
Formatter() = default;
explicit Formatter(StandardFormatter formatter)
: StandardFormatter(formatter)
{
}
void format(FormatBuilder&, long double value);
};
#endif
template<>