diff --git a/AK/Format.cpp b/AK/Format.cpp index 5385d5552f8..30943e57825 100644 --- a/AK/Format.cpp +++ b/AK/Format.cpp @@ -380,6 +380,53 @@ void FormatBuilder::put_f64( put_string(string_builder.string_view(), align, min_width, NumericLimits::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(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 : Making Your Code 10x Faster With C++17's Final Boss”) + value -= static_cast(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(value) < epsilon) + break; + value *= 10.0; + epsilon *= 10.0; + } + + if (visible_precision > 0) { + string_builder.append('.'); + format_builder.put_u64(static_cast(value), base, false, upper_case, true, Align::Right, visible_precision); + } + } + + put_string(string_builder.string_view(), align, min_width, NumericLimits::max(), fill); +} + #endif void FormatBuilder::put_hexdump(ReadonlyBytes bytes, size_t width, char fill) @@ -608,6 +655,29 @@ void Formatter::format(FormatBuilder& builder, bool value) } } #ifndef KERNEL +void Formatter::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::format(FormatBuilder& builder, double value) { u8 base; diff --git a/AK/Format.h b/AK/Format.h index 3eb562752d1..dd0e75e4535 100644 --- a/AK/Format.h +++ b/AK/Format.h @@ -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 : StandardFormatter { void format(FormatBuilder&, double value); }; + +template<> +struct Formatter : StandardFormatter { + Formatter() = default; + explicit Formatter(StandardFormatter formatter) + : StandardFormatter(formatter) + { + } + + void format(FormatBuilder&, long double value); +}; #endif template<>