Use a custom formatter to limit float precision

This commit is contained in:
Evil Eye 2025-08-16 09:22:54 +02:00
parent d4a1061354
commit eb01a302f1
3 changed files with 73 additions and 9 deletions

View File

@ -30,6 +30,50 @@
#include "itemmodel.hpp"
namespace
{
template <unsigned short Precision = 2>
struct LimitedFloat
{
float mValue;
constexpr unsigned short getPrecision() { return Precision; }
};
}
template <>
struct std::formatter<LimitedFloat<>, char>
{
template <class ParseContext>
constexpr ParseContext::iterator parse(ParseContext& ctx)
{
return ctx.begin();
}
template <class FmtContext>
FmtContext::iterator format(LimitedFloat<> v, FmtContext& ctx) const
{
constexpr unsigned short precision = v.getPrecision();
// FLOAT_MAX needs 39 digits + 1 for the sign + 1 for the decimal separator
constexpr std::size_t bufferSize = 41 + precision;
char buffer[bufferSize];
std::to_chars_result result
= std::to_chars(buffer, buffer + bufferSize, v.mValue, std::chars_format::fixed, precision);
if (result.ec != std::errc())
{
// Should never happen, but just default to regular float formatting
return std::format_to(ctx.out(), "{}", v.mValue);
}
// Trim result so 1.00 turns into 1
char* end = result.ptr;
while (end > buffer && *(end - 1) == '0')
--end;
if (end > buffer && *(end - 1) == '.')
--end;
return std::ranges::copy(buffer, end, ctx.out()).out;
}
};
namespace MWGui
{
ToolTips::ToolTips()
@ -643,13 +687,7 @@ namespace MWGui
std::string ToolTips::toString(const float value)
{
std::ostringstream stream;
if (value != int(value))
stream << std::setprecision(3);
stream << value;
return stream.str();
return std::format("{}", LimitedFloat(value));
}
std::string ToolTips::toString(const int value)
@ -661,14 +699,14 @@ namespace MWGui
{
if (weight == 0)
return {};
return std::format("\n{}: {}", prefix, toString(weight));
return std::format("\n{}: {}", prefix, LimitedFloat(weight));
}
std::string ToolTips::getPercentString(const float value, std::string_view prefix)
{
if (value == 0)
return {};
return std::format("\n{}: {}%", prefix, toString(value * 100));
return std::format("\n{}: {}%", prefix, LimitedFloat(value * 100));
}
std::string ToolTips::getValueString(const int value, std::string_view prefix)

View File

@ -14,6 +14,8 @@ file(GLOB UNITTEST_SRC_FILES
mwdialogue/testkeywordsearch.cpp
mwgui/tooltips.cpp
mwscript/testscripts.cpp
)

View File

@ -0,0 +1,24 @@
#include <gtest/gtest.h>
#include "apps/openmw/mwgui/tooltips.hpp"
#include <limits>
namespace MWGui
{
namespace
{
TEST(MWGuiToolTipsTest, floatsShouldBeFormattedCorrectly)
{
EXPECT_EQ(ToolTips::toString(1.f), "1");
EXPECT_EQ(ToolTips::toString(1.1f), "1.1");
EXPECT_EQ(ToolTips::toString(1.12f), "1.12");
EXPECT_EQ(ToolTips::toString(1234567.12f), "1234567.12");
EXPECT_EQ(ToolTips::toString(0.001f), "0");
EXPECT_EQ(ToolTips::toString(0.01f), "0.01");
EXPECT_EQ(ToolTips::toString(0.01f), "0.01");
EXPECT_EQ(ToolTips::toString(std::numeric_limits<float>::infinity()), "inf");
EXPECT_EQ(ToolTips::toString(std::numeric_limits<float>::quiet_NaN()), "nan");
}
}
}