mirror of
https://github.com/ETLCPP/etl.git
synced 2026-04-30 19:09:10 +08:00
When formatting float, fix the -0.0 case format.h float format: Fix rounding issues on all platforms
782 lines
35 KiB
C++
782 lines
35 KiB
C++
/******************************************************************************
|
|
The MIT License(MIT)
|
|
|
|
Embedded Template Library.
|
|
https://github.com/ETLCPP/etl
|
|
https://www.etlcpp.com
|
|
|
|
Copyright(c) 2025 BMW AG
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
of this software and associated documentation files(the "Software"), to deal
|
|
in the Software without restriction, including without limitation the rights
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
furnished to do so, subject to the following conditions :
|
|
|
|
The above copyright notice and this permission notice shall be included in all
|
|
copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
SOFTWARE.
|
|
******************************************************************************/
|
|
|
|
#include "unit_test_framework.h"
|
|
|
|
#include "etl/format.h"
|
|
|
|
#include "etl/iterator.h"
|
|
|
|
#if ETL_USING_CPP11
|
|
|
|
namespace
|
|
{
|
|
using iterator = etl::back_insert_iterator<etl::istring>;
|
|
|
|
template <class... Args>
|
|
etl::istring& test_format(etl::istring& s, etl::format_string<Args...> fmt, Args&&... args)
|
|
{
|
|
(void)etl::format_to(s, fmt, etl::forward<Args>(args)...);
|
|
return s;
|
|
}
|
|
|
|
SUITE(test_format)
|
|
{
|
|
//*************************************************************************
|
|
TEST(test_empty)
|
|
{
|
|
etl::string<100> s;
|
|
|
|
const char* result = etl::format_to(s, "");
|
|
CHECK_EQUAL(s.cbegin(), result);
|
|
CHECK_EQUAL("", s);
|
|
}
|
|
|
|
//*************************************************************************
|
|
TEST(test_format)
|
|
{
|
|
etl::string<100> s;
|
|
|
|
const char* result = etl::format_to(s, "abc");
|
|
CHECK_EQUAL(s.cbegin() + 3, result);
|
|
CHECK_EQUAL("abc", s);
|
|
}
|
|
|
|
//*************************************************************************
|
|
TEST(test_format_int)
|
|
{
|
|
etl::string<100> s;
|
|
|
|
CHECK_EQUAL("1", test_format(s, "{}", 1));
|
|
CHECK_EQUAL("123", test_format(s, "{}", 123));
|
|
CHECK_EQUAL("4123", test_format(s, "{}", 4123));
|
|
CHECK_EQUAL("1048962123", test_format(s, "{}", 1048962123));
|
|
CHECK_EQUAL("1 2", test_format(s, "{} {}", 1, 2));
|
|
CHECK_EQUAL("-123", test_format(s, "{}", -123));
|
|
CHECK_EQUAL("-314748364", test_format(s, "{}", (int)-314748364));
|
|
CHECK_EQUAL("2147483647", test_format(s, "{}", INT32_MAX));
|
|
CHECK_EQUAL("-2147483648", test_format(s, "{}", INT32_MIN));
|
|
CHECK_EQUAL("0", test_format(s, "{}", 0));
|
|
CHECK_EQUAL("-1", test_format(s, "{}", -1));
|
|
}
|
|
|
|
//*************************************************************************
|
|
TEST(test_format_short)
|
|
{
|
|
etl::string<100> s;
|
|
|
|
CHECK_EQUAL("1", test_format(s, "{}", static_cast<short>(1)));
|
|
CHECK_EQUAL("123", test_format(s, "{}", static_cast<short>(123)));
|
|
CHECK_EQUAL("4123", test_format(s, "{}", static_cast<short>(4123)));
|
|
CHECK_EQUAL("1 2", test_format(s, "{} {}", static_cast<short>(1), static_cast<short>(2)));
|
|
CHECK_EQUAL("-123", test_format(s, "{}", static_cast<short>(-123)));
|
|
CHECK_EQUAL("0", test_format(s, "{}", static_cast<short>(0)));
|
|
CHECK_EQUAL("-1", test_format(s, "{}", static_cast<short>(-1)));
|
|
}
|
|
|
|
//*************************************************************************
|
|
TEST(test_format_unsigned_short)
|
|
{
|
|
etl::string<100> s;
|
|
|
|
CHECK_EQUAL("1", test_format(s, "{}", static_cast<unsigned short>(1)));
|
|
CHECK_EQUAL("123", test_format(s, "{}", static_cast<unsigned short>(123)));
|
|
CHECK_EQUAL("4123", test_format(s, "{}", static_cast<unsigned short>(4123)));
|
|
CHECK_EQUAL("1 2", test_format(s, "{} {}", static_cast<unsigned short>(1), static_cast<unsigned short>(2)));
|
|
CHECK_EQUAL("60123", test_format(s, "{}", static_cast<unsigned short>(60123)));
|
|
CHECK_EQUAL("0", test_format(s, "{}", static_cast<unsigned short>(0)));
|
|
CHECK_EQUAL("65500", test_format(s, "{}", static_cast<unsigned short>(65500)));
|
|
}
|
|
|
|
//*************************************************************************
|
|
TEST(test_format_long_int)
|
|
{
|
|
etl::string<100> s;
|
|
|
|
CHECK_EQUAL("1", test_format(s, "{}", static_cast<long int>(1)));
|
|
CHECK_EQUAL("123", test_format(s, "{}", static_cast<long int>(123)));
|
|
CHECK_EQUAL("4123", test_format(s, "{}", static_cast<long int>(4123)));
|
|
CHECK_EQUAL("1 2", test_format(s, "{} {}", static_cast<long int>(1), static_cast<long int>(2)));
|
|
CHECK_EQUAL("-123", test_format(s, "{}", static_cast<long int>(-123)));
|
|
CHECK_EQUAL("0", test_format(s, "{}", static_cast<long int>(0)));
|
|
CHECK_EQUAL("-1", test_format(s, "{}", static_cast<long int>(-1)));
|
|
}
|
|
|
|
//*************************************************************************
|
|
TEST(test_format_unsigned_long_int)
|
|
{
|
|
etl::string<100> s;
|
|
|
|
CHECK_EQUAL("1", test_format(s, "{}", static_cast<unsigned long int>(1)));
|
|
CHECK_EQUAL("123", test_format(s, "{}", static_cast<unsigned long int>(123)));
|
|
CHECK_EQUAL("4123", test_format(s, "{}", static_cast<unsigned long int>(4123)));
|
|
CHECK_EQUAL("1 2", test_format(s, "{} {}", static_cast<unsigned long int>(1), static_cast<unsigned long int>(2)));
|
|
CHECK_EQUAL("60123", test_format(s, "{}", static_cast<unsigned long int>(60123)));
|
|
CHECK_EQUAL("0", test_format(s, "{}", static_cast<unsigned long int>(0)));
|
|
CHECK_EQUAL("65500", test_format(s, "{}", static_cast<unsigned long int>(65500)));
|
|
}
|
|
|
|
//*************************************************************************
|
|
TEST(test_format_unsigned_int)
|
|
{
|
|
etl::string<100> s;
|
|
|
|
CHECK_EQUAL("0", test_format(s, "{}", static_cast<unsigned int>(0U)));
|
|
CHECK_EQUAL("1", test_format(s, "{}", static_cast<unsigned int>(1U)));
|
|
CHECK_EQUAL("12345678", test_format(s, "{}", static_cast<unsigned int>(12345678U)));
|
|
CHECK_EQUAL("4123456780", test_format(s, "{}", static_cast<unsigned int>(4123456780U)));
|
|
}
|
|
|
|
//*************************************************************************
|
|
TEST(test_format_long_long_int)
|
|
{
|
|
etl::string<100> s;
|
|
|
|
CHECK_EQUAL("-1", test_format(s, "{}", static_cast<long long int>(-1LL)));
|
|
CHECK_EQUAL("0", test_format(s, "{}", static_cast<long long int>(0LL)));
|
|
CHECK_EQUAL("1", test_format(s, "{}", static_cast<long long int>(1LL)));
|
|
CHECK_EQUAL("-12345678", test_format(s, "{}", static_cast<long long int>(-12345678LL)));
|
|
CHECK_EQUAL("-4123456780", test_format(s, "{}", static_cast<long long int>(-4123456780LL)));
|
|
CHECK_EQUAL("-123456781234", test_format(s, "{}", static_cast<long long int>(-123456781234LL)));
|
|
CHECK_EQUAL("-412345678012", test_format(s, "{}", static_cast<long long int>(-412345678012LL)));
|
|
CHECK_EQUAL("12345678", test_format(s, "{}", static_cast<long long int>(12345678LL)));
|
|
CHECK_EQUAL("4123456780", test_format(s, "{}", static_cast<long long int>(4123456780LL)));
|
|
}
|
|
|
|
//*************************************************************************
|
|
TEST(test_format_unsigned_long_long_int)
|
|
{
|
|
etl::string<100> s;
|
|
|
|
CHECK_EQUAL("0", test_format(s, "{}", static_cast<unsigned long long int>(0LL)));
|
|
CHECK_EQUAL("1", test_format(s, "{}", static_cast<unsigned long long int>(1LL)));
|
|
CHECK_EQUAL("12345678", test_format(s, "{}", static_cast<unsigned long long int>(12345678LL)));
|
|
CHECK_EQUAL("4123456780", test_format(s, "{}", static_cast<unsigned long long int>(4123456780LL)));
|
|
CHECK_EQUAL("18446744073709551615", test_format(s, "{}", static_cast<unsigned long long int>(18446744073709551615ULL)));
|
|
CHECK_EQUAL("1311768467463790320", test_format(s, "{}", static_cast<unsigned long long int>(0x123456789ABCDEF0ULL)));
|
|
}
|
|
|
|
//*************************************************************************
|
|
TEST(test_format_other_int)
|
|
{
|
|
etl::string<100> s;
|
|
|
|
// mapped to unsigned char
|
|
// CHECK_EQUAL("34", test_format(s, "{}", static_cast<uint8_t>(34)));
|
|
// mapped to signed char
|
|
// CHECK_EQUAL("-14", test_format(s, "{}", static_cast<int8_t>(-14)));
|
|
CHECK_EQUAL("6534", test_format(s, "{}", static_cast<uint16_t>(6534)));
|
|
CHECK_EQUAL("-9414", test_format(s, "{}", static_cast<int16_t>(-9414)));
|
|
CHECK_EQUAL("236534", test_format(s, "{}", static_cast<uint32_t>(236534)));
|
|
CHECK_EQUAL("-6759414", test_format(s, "{}", static_cast<int32_t>(-6759414)));
|
|
}
|
|
|
|
#if ETL_USING_FORMAT_FLOATING_POINT
|
|
//*************************************************************************
|
|
TEST(test_format_float)
|
|
{
|
|
etl::string<100> s;
|
|
|
|
CHECK_EQUAL("1.0", test_format(s, "{}", 1.0f));
|
|
CHECK_EQUAL("1.234567", test_format(s, "{}", 1.2345674f));
|
|
CHECK_EQUAL("1.234568", test_format(s, "{}", 1.2345676f));
|
|
CHECK_EQUAL("1.125", test_format(s, "{}", 1.125f));
|
|
}
|
|
|
|
//*************************************************************************
|
|
TEST(test_format_double)
|
|
{
|
|
etl::string<100> s;
|
|
|
|
CHECK_EQUAL("1.0", test_format(s, "{}", 1.0));
|
|
CHECK_EQUAL("1.234567", test_format(s, "{}", 1.234567499));
|
|
CHECK_EQUAL("1.234568", test_format(s, "{}", 1.234567501));
|
|
CHECK_EQUAL("1.5", test_format(s, "{}", 1.5));
|
|
}
|
|
|
|
//*************************************************************************
|
|
TEST(test_format_long_double)
|
|
{
|
|
etl::string<100> s;
|
|
|
|
CHECK_EQUAL("1.0", test_format(s, "{}", 1.0l));
|
|
auto& result = test_format(s, "{}", 1.234567l);
|
|
CHECK("1.234567" == result || "1.234566" == result);
|
|
CHECK_EQUAL("1.234568", test_format(s, "{}", 1.2345678l));
|
|
CHECK_EQUAL("1.25", test_format(s, "{}", 1.25l));
|
|
}
|
|
|
|
//*************************************************************************
|
|
TEST(test_format_float_presentation)
|
|
{
|
|
etl::string<100> s;
|
|
|
|
CHECK_EQUAL("1.000000e+00", test_format(s, "{:e}", 1.0f));
|
|
CHECK_EQUAL("1.125000E+00", test_format(s, "{:E}", 1.125f));
|
|
CHECK_EQUAL("-2.533324e-05", test_format(s, "{:e}", -0.00002533324f));
|
|
CHECK_EQUAL("-2.500000e+11", test_format(s, "{:e}", -250000000000.0f));
|
|
CHECK_EQUAL("1.000000", test_format(s, "{:f}", 1.0f));
|
|
CHECK_EQUAL("1.125000", test_format(s, "{:F}", 1.125f));
|
|
CHECK_EQUAL("1.000000", test_format(s, "{:g}", 1.0f));
|
|
CHECK_EQUAL("1.125000", test_format(s, "{:G}", 1.125f));
|
|
CHECK_EQUAL("1.000000e+10", test_format(s, "{:g}", 10000000000.0f));
|
|
CHECK_EQUAL("1.000000E+10", test_format(s, "{:G}", 10000000000.0f));
|
|
CHECK_EQUAL("nan", test_format(s, "{}", NAN));
|
|
CHECK_EQUAL("nan", test_format(s, "{:e}", NAN));
|
|
CHECK_EQUAL("NAN", test_format(s, "{:E}", NAN));
|
|
CHECK_EQUAL("nan", test_format(s, "{:f}", NAN));
|
|
CHECK_EQUAL("NAN", test_format(s, "{:F}", NAN));
|
|
CHECK_EQUAL("nan", test_format(s, "{:g}", NAN));
|
|
CHECK_EQUAL("NAN", test_format(s, "{:0.3G}", NAN));
|
|
CHECK_EQUAL("inf", test_format(s, "{}", INFINITY));
|
|
CHECK_EQUAL("inf", test_format(s, "{:e}", INFINITY));
|
|
CHECK_EQUAL("INF", test_format(s, "{:E}", INFINITY));
|
|
CHECK_EQUAL("inf", test_format(s, "{:f}", INFINITY));
|
|
CHECK_EQUAL("INF", test_format(s, "{:F}", INFINITY));
|
|
CHECK_EQUAL("inf", test_format(s, "{:g}", INFINITY));
|
|
CHECK_EQUAL("INF", test_format(s, "{:0.3G}", INFINITY));
|
|
CHECK_EQUAL("0x1.8p+0", test_format(s, "{:a}", 1.5f));
|
|
CHECK_EQUAL("0X1.4CCCCCCCCDP+0", test_format(s, "{:A}", 1.3l));
|
|
CHECK_EQUAL("0x2.49fp+4", test_format(s, "{:a}", 150000.0));
|
|
CHECK_EQUAL("0x1.92a738p-5", test_format(s, "{:a}", 0.0000015f));
|
|
CHECK_EQUAL("0x1.6345785d8ap+e", test_format(s, "{:a}", 100000000000000000.l));
|
|
}
|
|
|
|
//*************************************************************************
|
|
TEST(test_format_negative_zero)
|
|
{
|
|
etl::string<100> s;
|
|
|
|
// Test negative zero handling - signbit() correctly detects -0.0
|
|
CHECK_EQUAL("-0.0", test_format(s, "{}", -0.0));
|
|
CHECK_EQUAL("-0.0", test_format(s, "{}", -0.0f));
|
|
CHECK_EQUAL("-0.0", test_format(s, "{}", -0.0L));
|
|
CHECK_EQUAL("-0.000000", test_format(s, "{:f}", -0.0));
|
|
CHECK_EQUAL("-0.000000", test_format(s, "{:f}", -0.0f));
|
|
CHECK_EQUAL("-0.000000", test_format(s, "{:f}", -0.0L));
|
|
CHECK_EQUAL("-0.000000e+00", test_format(s, "{:e}", -0.0));
|
|
CHECK_EQUAL("-0.000000e+00", test_format(s, "{:e}", -0.0f));
|
|
CHECK_EQUAL("-0.000000e+00", test_format(s, "{:e}", -0.0L));
|
|
CHECK_EQUAL("-0x0.0p+0", test_format(s, "{:a}", -0.0));
|
|
CHECK_EQUAL("-0x0.0p+0", test_format(s, "{:a}", -0.0f));
|
|
CHECK_EQUAL("-0x0.0p+0", test_format(s, "{:a}", -0.0L));
|
|
}
|
|
|
|
//*************************************************************************
|
|
// Tests for fractional rounding carry:
|
|
// When round(fractional * scale) == scale the fractional part must wrap
|
|
// to 0 and the integral part must be incremented by 1.
|
|
//*************************************************************************
|
|
TEST(test_format_floating_default_rounding_carry)
|
|
{
|
|
etl::string<100> s;
|
|
|
|
// 1.9999999: fractional 0.9999999, round(0.9999999 * 1e6) == 1000000
|
|
// => must carry: integral 1 -> 2, fractional -> 0
|
|
CHECK_EQUAL("2.0", test_format(s, "{}", 1.9999999));
|
|
CHECK_EQUAL("-2.0", test_format(s, "{}", -1.9999999));
|
|
|
|
// 0.9999999: integral 0, fractional rounds up => becomes 1.0
|
|
CHECK_EQUAL("1.0", test_format(s, "{}", 0.9999999));
|
|
|
|
// 99.9999999: integral 99, fractional rounds up => becomes 100.0
|
|
CHECK_EQUAL("100.0", test_format(s, "{}", 99.9999999));
|
|
}
|
|
|
|
//*************************************************************************
|
|
TEST(test_format_floating_f_rounding_carry)
|
|
{
|
|
etl::string<100> s;
|
|
|
|
// Same values using {:f} which uses format_floating_f (6 fractional decimals)
|
|
CHECK_EQUAL("2.000000", test_format(s, "{:f}", 1.9999999));
|
|
CHECK_EQUAL("-2.000000", test_format(s, "{:f}", -1.9999999));
|
|
CHECK_EQUAL("1.000000", test_format(s, "{:f}", 0.9999999));
|
|
CHECK_EQUAL("100.000000", test_format(s, "{:f}", 99.9999999));
|
|
}
|
|
|
|
//*************************************************************************
|
|
TEST(test_format_floating_e_rounding_carry)
|
|
{
|
|
etl::string<100> s;
|
|
|
|
// 9.9999999: after normalization integral=9, fractional=0.9999999
|
|
// round(0.9999999 * 1e6) == 1000000 => must carry: 10.000000e+00
|
|
CHECK_EQUAL("10.000000e+00", test_format(s, "{:e}", 9.9999999));
|
|
CHECK_EQUAL("-10.000000e+00", test_format(s, "{:e}", -9.9999999));
|
|
|
|
// 1.9999999: after normalization integral=1, fractional=0.9999999
|
|
CHECK_EQUAL("2.000000e+00", test_format(s, "{:e}", 1.9999999));
|
|
}
|
|
|
|
//*************************************************************************
|
|
TEST(test_format_floating_a_rounding_carry)
|
|
{
|
|
etl::string<100> s;
|
|
|
|
// 1.5 + (1.0 - 2^-52) * 0.5 ≈ value whose hex fractional part rounds up.
|
|
// Use a value where hex fractional is all 0xF...F and rounds up.
|
|
// 2.0 - epsilon: in hex, 0x1.FFFFFFFFFFFFFp+0 (for double)
|
|
// After modf: integral=1, fractional ≈ 0.999...
|
|
// round(fractional * 16^10) should equal 16^10 => carry
|
|
double almost_two = 1.9999999999999998; // nextafter(2.0, 0.0) or very close
|
|
auto& result = test_format(s, "{:a}", almost_two);
|
|
// After carry, integral becomes 2, fractional becomes 0
|
|
CHECK_EQUAL("0x2.0p+0", result);
|
|
}
|
|
#endif
|
|
|
|
//*************************************************************************
|
|
TEST(test_format_char_array)
|
|
{
|
|
etl::string<100> s;
|
|
|
|
CHECK_EQUAL("s", test_format(s, "{}", "s"));
|
|
CHECK_EQUAL("abcd", test_format(s, "{}", "abcd"));
|
|
}
|
|
|
|
//*************************************************************************
|
|
TEST(test_format_char)
|
|
{
|
|
etl::string<100> s;
|
|
|
|
CHECK_EQUAL("a s b", test_format(s, "a {} b", 's'));
|
|
CHECK_EQUAL("a s b", test_format(s, "a {:c} b", 's'));
|
|
CHECK_EQUAL("a 's' b", test_format(s, "a {:?} b", 's'));
|
|
CHECK_EQUAL("a \t b", test_format(s, "a {} b", '\t'));
|
|
CHECK_EQUAL("a '\\t' b", test_format(s, "a {:?} b", '\t'));
|
|
CHECK_EQUAL("a '\\n' b", test_format(s, "a {:?} b", '\n'));
|
|
CHECK_EQUAL("a '\\r' b", test_format(s, "a {:?} b", '\r'));
|
|
CHECK_EQUAL("a '\\\"' b", test_format(s, "a {:?} b", '"'));
|
|
CHECK_EQUAL("a '\\'' b", test_format(s, "a {:?} b", '\''));
|
|
CHECK_EQUAL("a '\\\\' b", test_format(s, "a {:?} b", '\\'));
|
|
CHECK_EQUAL("a '\\\\' b", test_format(s, "a {:?} b", '\\'));
|
|
CHECK_EQUAL("a 97 b", test_format(s, "a {:d} b", 'a'));
|
|
CHECK_EQUAL("a 61 b", test_format(s, "a {:X} b", 'a'));
|
|
CHECK_EQUAL("a 61 b", test_format(s, "a {:x} b", 'a'));
|
|
CHECK_EQUAL("a 0x61 b", test_format(s, "a {:#x} b", 'a'));
|
|
}
|
|
|
|
//*************************************************************************
|
|
TEST(test_format_bool)
|
|
{
|
|
etl::string<100> s;
|
|
|
|
CHECK_EQUAL("1false2true3", test_format(s, "1{}2{}3", false, true));
|
|
CHECK_EQUAL("true", test_format(s, "{:s}", true));
|
|
CHECK_EQUAL("1", test_format(s, "{:d}", true));
|
|
CHECK_EQUAL("1", test_format(s, "{:X}", true));
|
|
CHECK_EQUAL("01", test_format(s, "{:#o}", true));
|
|
CHECK_EQUAL(" true", test_format(s, "{:10}", true));
|
|
}
|
|
|
|
//*************************************************************************
|
|
TEST(test_format_string_view)
|
|
{
|
|
etl::string<100> s;
|
|
etl::string_view sv("data1");
|
|
|
|
CHECK_EQUAL("data1", test_format(s, "{}", sv));
|
|
CHECK_EQUAL("data1", test_format(s, "{:s}", sv));
|
|
CHECK_THROW(test_format(s, "{:d}", sv), etl::bad_format_string_exception);
|
|
CHECK_EQUAL("data1 ", test_format(s, "{:10s}", sv));
|
|
CHECK_EQUAL("data1 ", test_format(s, "{:<10s}", sv));
|
|
CHECK_EQUAL(" data1", test_format(s, "{:>10s}", sv));
|
|
CHECK_EQUAL(" data1 ", test_format(s, "{:^10s}", sv));
|
|
CHECK_EQUAL("data1", test_format(s, "{:3}", sv));
|
|
CHECK_EQUAL("dat", test_format(s, "{:.3s}", sv));
|
|
CHECK_EQUAL("dat", test_format(s, "{:^.3s}", sv));
|
|
CHECK_EQUAL(". dat !", test_format(s, ".{:^8.3s}!", sv));
|
|
CHECK_EQUAL("^dat $", test_format(s, "^{:8.3s}$", sv));
|
|
}
|
|
|
|
//*************************************************************************
|
|
TEST(test_format_string_view_escaped)
|
|
{
|
|
etl::string<100> s;
|
|
etl::string_view sv("data1\n");
|
|
|
|
CHECK_EQUAL("\"data1\\n\"", test_format(s, "{:?}", sv));
|
|
}
|
|
|
|
//*************************************************************************
|
|
TEST(test_format_string)
|
|
{
|
|
etl::string<100> s;
|
|
etl::string<10> s_arg = "data1";
|
|
|
|
CHECK_EQUAL("data1", test_format(s, "{}", s_arg));
|
|
CHECK_EQUAL("data1", test_format(s, "{:s}", s_arg));
|
|
CHECK_THROW(test_format(s, "{:d}", s_arg), etl::bad_format_string_exception);
|
|
CHECK_EQUAL("data1 ", test_format(s, "{:10s}", s_arg));
|
|
CHECK_EQUAL("data1 ", test_format(s, "{:<10s}", s_arg));
|
|
CHECK_EQUAL(" data1", test_format(s, "{:>10s}", s_arg));
|
|
CHECK_EQUAL(" data1 ", test_format(s, "{:^10s}", s_arg));
|
|
CHECK_EQUAL("data1", test_format(s, "{:3}", s_arg));
|
|
CHECK_EQUAL("dat", test_format(s, "{:.3s}", s_arg));
|
|
CHECK_EQUAL("dat", test_format(s, "{:^.3s}", s_arg));
|
|
CHECK_EQUAL(". dat !", test_format(s, ".{:^8.3s}!", s_arg));
|
|
CHECK_EQUAL("^dat $", test_format(s, "^{:8.3s}$", s_arg));
|
|
}
|
|
|
|
//*************************************************************************
|
|
// this minimal derived class of etl::string is used to prove that the
|
|
// temporary lifetime is long enough for the format operation
|
|
template <size_t N>
|
|
class clearing_string : public etl::string<N>
|
|
{
|
|
public:
|
|
|
|
using etl::string<N>::string;
|
|
~clearing_string()
|
|
{
|
|
this->clear();
|
|
}
|
|
};
|
|
TEST(test_format_string_temporary)
|
|
{
|
|
etl::string<100> s;
|
|
const char* data = "data1";
|
|
using string_t = clearing_string<10>;
|
|
|
|
CHECK_EQUAL("data1", test_format(s, "{}", string_t(data)));
|
|
CHECK_EQUAL("data1", test_format(s, "{:s}", string_t(data)));
|
|
CHECK_THROW(test_format(s, "{:d}", string_t(data)), etl::bad_format_string_exception);
|
|
CHECK_EQUAL("data1 ", test_format(s, "{:10s}", string_t(data)));
|
|
CHECK_EQUAL("data1 ", test_format(s, "{:<10s}", string_t(data)));
|
|
CHECK_EQUAL(" data1", test_format(s, "{:>10s}", string_t(data)));
|
|
CHECK_EQUAL(" data1 ", test_format(s, "{:^10s}", string_t(data)));
|
|
CHECK_EQUAL("data1", test_format(s, "{:3}", string_t(data)));
|
|
CHECK_EQUAL("dat", test_format(s, "{:.3s}", string_t(data)));
|
|
CHECK_EQUAL("dat", test_format(s, "{:^.3s}", string_t(data)));
|
|
CHECK_EQUAL(". dat !", test_format(s, ".{:^8.3s}!", string_t(data)));
|
|
CHECK_EQUAL("^dat $", test_format(s, "^{:8.3s}$", string_t(data)));
|
|
}
|
|
|
|
//*************************************************************************
|
|
TEST(test_format_string_escaped)
|
|
{
|
|
etl::string<100> s;
|
|
etl::string<10> s_arg("data1\n");
|
|
|
|
CHECK_EQUAL("\"data1\\n\"", test_format(s, "{:?}", s_arg));
|
|
}
|
|
|
|
//*************************************************************************
|
|
TEST(test_format_chars)
|
|
{
|
|
etl::string<100> s;
|
|
const char* chars = "data1";
|
|
|
|
CHECK_EQUAL("data1", test_format(s, "{}", chars));
|
|
CHECK_EQUAL("data1", test_format(s, "{:s}", chars));
|
|
CHECK_THROW(test_format(s, "{:d}", chars), etl::bad_format_string_exception);
|
|
CHECK_EQUAL("data1 ", test_format(s, "{:10s}", chars));
|
|
CHECK_EQUAL("data1 ", test_format(s, "{:<10s}", chars));
|
|
CHECK_EQUAL(" data1", test_format(s, "{:>10s}", chars));
|
|
CHECK_EQUAL(" data1 ", test_format(s, "{:^10s}", chars));
|
|
CHECK_EQUAL("data1", test_format(s, "{:3}", chars));
|
|
CHECK_EQUAL("dat", test_format(s, "{:.3s}", chars));
|
|
CHECK_EQUAL("dat", test_format(s, "{:^.3s}", chars));
|
|
CHECK_EQUAL(". dat !", test_format(s, ".{:^8.3s}!", chars));
|
|
CHECK_EQUAL("^dat $", test_format(s, "^{:8.3s}$", chars));
|
|
}
|
|
|
|
//*************************************************************************
|
|
TEST(test_format_chars_escaped)
|
|
{
|
|
etl::string<100> s;
|
|
const char* chars = "data2\n";
|
|
|
|
CHECK_EQUAL("\"data2\\n\"", test_format(s, "{:?}", chars));
|
|
}
|
|
|
|
//*************************************************************************
|
|
TEST(test_format_pointer)
|
|
{
|
|
etl::string<100> s;
|
|
|
|
void* p = nullptr;
|
|
|
|
CHECK_EQUAL("0x0", test_format(s, "{}", p));
|
|
CHECK_EQUAL("0x0", test_format(s, "{:p}", p));
|
|
CHECK_EQUAL("0X0", test_format(s, "{:P}", p));
|
|
|
|
if (sizeof(uintptr_t) == 8)
|
|
{
|
|
p = reinterpret_cast<void*>(0x123456789abcdef0ULL);
|
|
CHECK_EQUAL("0x123456789abcdef0", test_format(s, "{:p}", p));
|
|
CHECK_EQUAL("0X123456789ABCDEF0", test_format(s, "{:P}", p));
|
|
}
|
|
else if (sizeof(uintptr_t) == 4)
|
|
{
|
|
p = reinterpret_cast<void*>(0x1abcdef0ULL);
|
|
CHECK_EQUAL("0x1abcdef0", test_format(s, "{:p}", p));
|
|
CHECK_EQUAL("0X1ABCDEF0", test_format(s, "{:P}", p));
|
|
}
|
|
}
|
|
|
|
//*************************************************************************
|
|
TEST(test_format_size_t)
|
|
{
|
|
etl::string<100> s;
|
|
|
|
CHECK_EQUAL("0", test_format(s, "{}", static_cast<size_t>(0LL)));
|
|
CHECK_EQUAL("1", test_format(s, "{}", static_cast<size_t>(1LL)));
|
|
CHECK_EQUAL("12345678", test_format(s, "{}", static_cast<size_t>(12345678LL)));
|
|
CHECK_EQUAL("4123456780", test_format(s, "{}", static_cast<size_t>(4123456780LL)));
|
|
#if ETL_PLATFORM_64BIT
|
|
static_assert(sizeof(size_t) == 8, "size_t is expected to be 64 bit on 64 bit platforms");
|
|
CHECK_EQUAL("18446744073709551615", test_format(s, "{}", static_cast<size_t>(18446744073709551615ULL)));
|
|
CHECK_EQUAL("1311768467463790320", test_format(s, "{}", static_cast<size_t>(0x123456789ABCDEF0ULL)));
|
|
#endif
|
|
}
|
|
|
|
//*************************************************************************
|
|
TEST(test_format_unsigned_long)
|
|
{
|
|
etl::string<100> s;
|
|
|
|
CHECK_EQUAL("0", test_format(s, "{}", static_cast<unsigned long>(0LL)));
|
|
CHECK_EQUAL("1", test_format(s, "{}", static_cast<unsigned long>(1LL)));
|
|
CHECK_EQUAL("12345678", test_format(s, "{}", static_cast<unsigned long>(12345678LL)));
|
|
CHECK_EQUAL("4123456780", test_format(s, "{}", static_cast<unsigned long>(4123456780LL)));
|
|
#if ETL_PLATFORM_64BIT
|
|
static_assert(sizeof(unsigned long) == 8, "size_t is expected to be 64 bit on 64 bit platforms");
|
|
CHECK_EQUAL("18446744073709551615", test_format(s, "{}", static_cast<unsigned long>(18446744073709551615ULL)));
|
|
CHECK_EQUAL("1311768467463790320", test_format(s, "{}", static_cast<unsigned long>(0x123456789ABCDEF0ULL)));
|
|
#endif
|
|
}
|
|
|
|
//*************************************************************************
|
|
TEST(test_format_signed_char)
|
|
{
|
|
etl::string<100> s;
|
|
|
|
CHECK_EQUAL("a s b", test_format(s, "a {} b", static_cast<signed char>('s')));
|
|
CHECK_EQUAL("a s b", test_format(s, "a {:c} b", static_cast<signed char>('s')));
|
|
CHECK_EQUAL("a 's' b", test_format(s, "a {:?} b", static_cast<signed char>('s')));
|
|
CHECK_EQUAL("a \t b", test_format(s, "a {} b", static_cast<signed char>('\t')));
|
|
CHECK_EQUAL("a '\\t' b", test_format(s, "a {:?} b", static_cast<signed char>('\t')));
|
|
CHECK_EQUAL("a '\\n' b", test_format(s, "a {:?} b", static_cast<signed char>('\n')));
|
|
CHECK_EQUAL("a '\\r' b", test_format(s, "a {:?} b", static_cast<signed char>('\r')));
|
|
CHECK_EQUAL("a '\\\"' b", test_format(s, "a {:?} b", static_cast<signed char>('"')));
|
|
CHECK_EQUAL("a '\\'' b", test_format(s, "a {:?} b", static_cast<signed char>('\'')));
|
|
CHECK_EQUAL("a '\\\\' b", test_format(s, "a {:?} b", static_cast<signed char>('\\')));
|
|
CHECK_EQUAL("a '\\\\' b", test_format(s, "a {:?} b", static_cast<signed char>('\\')));
|
|
CHECK_EQUAL("a 97 b", test_format(s, "a {:d} b", static_cast<signed char>('a')));
|
|
CHECK_EQUAL("a 61 b", test_format(s, "a {:X} b", static_cast<signed char>('a')));
|
|
CHECK_EQUAL("a 61 b", test_format(s, "a {:x} b", static_cast<signed char>('a')));
|
|
CHECK_EQUAL("a 0x61 b", test_format(s, "a {:#x} b", static_cast<signed char>('a')));
|
|
}
|
|
|
|
//*************************************************************************
|
|
TEST(test_format_unsigned_char)
|
|
{
|
|
etl::string<100> s;
|
|
|
|
CHECK_EQUAL("a s b", test_format(s, "a {} b", static_cast<unsigned char>('s')));
|
|
CHECK_EQUAL("a s b", test_format(s, "a {:c} b", static_cast<unsigned char>('s')));
|
|
CHECK_EQUAL("a 's' b", test_format(s, "a {:?} b", static_cast<unsigned char>('s')));
|
|
CHECK_EQUAL("a \t b", test_format(s, "a {} b", static_cast<unsigned char>('\t')));
|
|
CHECK_EQUAL("a '\\t' b", test_format(s, "a {:?} b", static_cast<unsigned char>('\t')));
|
|
CHECK_EQUAL("a '\\n' b", test_format(s, "a {:?} b", static_cast<unsigned char>('\n')));
|
|
CHECK_EQUAL("a '\\r' b", test_format(s, "a {:?} b", static_cast<unsigned char>('\r')));
|
|
CHECK_EQUAL("a '\\\"' b", test_format(s, "a {:?} b", static_cast<unsigned char>('"')));
|
|
CHECK_EQUAL("a '\\'' b", test_format(s, "a {:?} b", static_cast<unsigned char>('\'')));
|
|
CHECK_EQUAL("a '\\\\' b", test_format(s, "a {:?} b", static_cast<unsigned char>('\\')));
|
|
CHECK_EQUAL("a '\\\\' b", test_format(s, "a {:?} b", static_cast<unsigned char>('\\')));
|
|
CHECK_EQUAL("a 97 b", test_format(s, "a {:d} b", static_cast<unsigned char>('a')));
|
|
CHECK_EQUAL("a 61 b", test_format(s, "a {:X} b", static_cast<unsigned char>('a')));
|
|
CHECK_EQUAL("a 61 b", test_format(s, "a {:x} b", static_cast<unsigned char>('a')));
|
|
CHECK_EQUAL("a 0x61 b", test_format(s, "a {:#x} b", static_cast<unsigned char>('a')));
|
|
}
|
|
|
|
//*************************************************************************
|
|
TEST(test_format_limit)
|
|
{
|
|
etl::string<10> s;
|
|
|
|
CHECK_EQUAL("abcdefghij", test_format(s, "abcdefghijkl"));
|
|
CHECK_EQUAL("abcdefgh12", test_format(s, "abcdefgh{}", 123));
|
|
}
|
|
|
|
//*************************************************************************
|
|
TEST(test_format_escape)
|
|
{
|
|
etl::string<100> s;
|
|
|
|
CHECK_EQUAL("abc{def", test_format(s, "abc{{def"));
|
|
CHECK_EQUAL("}abc", test_format(s, "}}abc"));
|
|
}
|
|
|
|
//*************************************************************************
|
|
TEST(test_format_invalid)
|
|
{
|
|
etl::string<100> s;
|
|
|
|
CHECK_THROW(test_format(s, "a{b}", 1),
|
|
etl::bad_format_string_exception); // bad format index spec
|
|
// goal: rejected at compile time on C++20, error on <= C++17
|
|
|
|
CHECK_THROW(test_format(s, "a{b"),
|
|
etl::bad_format_string_exception); // closing brace missing
|
|
// goal: rejected at compile time on C++20, error on <= C++17
|
|
|
|
CHECK_THROW(test_format(s, "a{b}"),
|
|
etl::bad_format_string_exception); // arg missing
|
|
// goal: rejected at compile time on C++20, error on <= C++17
|
|
|
|
CHECK_THROW(test_format(s, "a}b"),
|
|
etl::bad_format_string_exception); // bad format: only escaped
|
|
// }} allowed
|
|
// goal: rejected at compile time on C++20, error on <= C++17
|
|
|
|
CHECK_EQUAL("123", test_format(s, "{:}", 123)); // valid
|
|
CHECK_THROW(test_format(s, "{::}", 123),
|
|
etl::bad_format_string_exception); // bad format spec
|
|
CHECK_THROW(test_format(s, "{1}", 123),
|
|
etl::bad_format_string_exception); // bad index
|
|
}
|
|
|
|
//*************************************************************************
|
|
TEST(test_format_indexed)
|
|
{
|
|
etl::string<100> s;
|
|
|
|
CHECK_EQUAL("34 56", test_format(s, "{0} {1}", 34, 56));
|
|
CHECK_EQUAL("56 34", test_format(s, "{1} {0}", 34, 56));
|
|
CHECK_EQUAL("134 134", test_format(s, "{0} {0}", 134));
|
|
CHECK_EQUAL("a b c d e f g h i j k l m n", test_format(s, "{0} {1} {2} {3} {4} {5} {6} {7} {8} {9} {10} {11} {12} {13}", 'a', 'b', 'c', 'd',
|
|
'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n'));
|
|
}
|
|
|
|
//*************************************************************************
|
|
TEST(test_formatted_size)
|
|
{
|
|
CHECK_EQUAL(0, etl::formatted_size(""));
|
|
CHECK_EQUAL(0, etl::formatted_size("{}", ""));
|
|
CHECK_EQUAL(5, etl::formatted_size("xyz{}", 12));
|
|
CHECK_EQUAL(3, etl::formatted_size("{}", "abc"));
|
|
}
|
|
|
|
//*************************************************************************
|
|
TEST(test_iterator)
|
|
{
|
|
etl::string<100> s;
|
|
|
|
etl::istring::iterator result = etl::format_to(s.begin(), "{0} {1}", 34, 56);
|
|
s.uninitialized_resize(static_cast<size_t>(result - s.begin()));
|
|
CHECK_EQUAL("34 56", s);
|
|
|
|
s.clear();
|
|
etl::format_to(iterator(s), "");
|
|
CHECK_EQUAL("", s);
|
|
|
|
s.clear();
|
|
etl::format_to(iterator(s), "{0} {1}", 65, 34);
|
|
CHECK_EQUAL("65 34", s);
|
|
|
|
s = "abcdefghij";
|
|
etl::format_to_n(s.begin(), 3, "xy{}", 123);
|
|
CHECK_EQUAL("xy1defghij", s);
|
|
}
|
|
|
|
//*************************************************************************
|
|
TEST(test_format_spec_fill_and_align)
|
|
{
|
|
etl::string<100> s;
|
|
|
|
CHECK_EQUAL("a 34", test_format(s, "a{:5}", 34));
|
|
CHECK_EQUAL("a34", test_format(s, "a{:1}", 34));
|
|
CHECK_EQUAL("a34", test_format(s, "a{:2}", 34));
|
|
CHECK_EQUAL(" 34", test_format(s, "{:>4}", 34));
|
|
CHECK_EQUAL(" 34 ", test_format(s, "{:^4}", 34));
|
|
CHECK_EQUAL(" 34 ", test_format(s, "{:^5}", 34));
|
|
CHECK_EQUAL(" -65 ", test_format(s, "{:^5}", -65));
|
|
CHECK_EQUAL("34 ", test_format(s, "{:<4}", 34));
|
|
CHECK_THROW(test_format(s, "a{:*5}", 34), etl::bad_format_string_exception);
|
|
CHECK_EQUAL("a*34**", test_format(s, "a{:*^5}", 34));
|
|
CHECK_EQUAL("a*34**", test_format(s, "a{:*^5}", static_cast<unsigned int>(34)));
|
|
CHECK_EQUAL("a***-341234567890****", test_format(s, "a{:*^20}", static_cast<long long int>(-341234567890)));
|
|
CHECK_EQUAL("a****341234567890****", test_format(s, "a{:*^20}", static_cast<unsigned long long int>(341234567890)));
|
|
CHECK_EQUAL(" x ", test_format(s, "{: ^20}", 'x'));
|
|
CHECK_EQUAL("x ", test_format(s, "{:20}", 'x'));
|
|
CHECK_EQUAL(" x", test_format(s, "{:>20}", 'x'));
|
|
CHECK_EQUAL("x ", test_format(s, "{:<20}", 'x'));
|
|
}
|
|
|
|
//*************************************************************************
|
|
TEST(test_format_spec_sign)
|
|
{
|
|
etl::string<100> s;
|
|
|
|
CHECK_EQUAL("34", test_format(s, "{:-}", 34));
|
|
CHECK_EQUAL("-34", test_format(s, "{:-}", -34));
|
|
CHECK_EQUAL("-34", test_format(s, "{:+}", -34));
|
|
CHECK_EQUAL("+34", test_format(s, "{:+}", 34));
|
|
CHECK_EQUAL("+0", test_format(s, "{:+}", 0));
|
|
CHECK_EQUAL("0", test_format(s, "{:-}", 0));
|
|
CHECK_EQUAL("210", test_format(s, "{:-}", 210));
|
|
CHECK_EQUAL("-210", test_format(s, "{:-}", -210));
|
|
CHECK_EQUAL(" 0", test_format(s, "{: }", 0));
|
|
CHECK_EQUAL(" 546", test_format(s, "{: }", 546));
|
|
CHECK_EQUAL("-546", test_format(s, "{: }", -546));
|
|
}
|
|
|
|
//*************************************************************************
|
|
TEST(test_format_int_presentation)
|
|
{
|
|
etl::string<100> s;
|
|
|
|
CHECK_EQUAL("134", test_format(s, "{:d}", 134));
|
|
CHECK_EQUAL("3f4", test_format(s, "{:x}", 0x3f4));
|
|
CHECK_EQUAL("123456789abcdef0", test_format(s, "{:x}", 0x123456789abcdef0ULL));
|
|
CHECK_EQUAL("0x3f4", test_format(s, "{:#x}", 0x3f4));
|
|
CHECK_EQUAL("3F4", test_format(s, "{:X}", 0x3f4));
|
|
CHECK_EQUAL("0X3F4", test_format(s, "{:#X}", 0x3f4));
|
|
CHECK_EQUAL("34", test_format(s, "{:o}", 034));
|
|
CHECK_EQUAL("034", test_format(s, "{:#o}", 034));
|
|
#if ETL_USING_CPP14
|
|
CHECK_EQUAL("1010", test_format(s, "{:b}", 0b1010));
|
|
CHECK_EQUAL("0b1010", test_format(s, "{:#b}", 0b1010));
|
|
CHECK_EQUAL("1010", test_format(s, "{:B}", 0b1010));
|
|
CHECK_EQUAL("0B1010", test_format(s, "{:#B}", 0b1010));
|
|
CHECK_EQUAL("-0B1010", test_format(s, "{:#B}", -0b1010));
|
|
#endif
|
|
CHECK_EQUAL("C", test_format(s, "{:c}", 67));
|
|
CHECK_EQUAL("00067", test_format(s, "{:05d}", 67));
|
|
CHECK_EQUAL("+00067", test_format(s, "{:+05d}", 67));
|
|
CHECK_EQUAL("+0X00EF1", test_format(s, "{:+#05X}", 0xEF1));
|
|
CHECK_THROW(test_format(s, "{:+#05.5X}", 0xEF1), etl::bad_format_string_exception);
|
|
}
|
|
}
|
|
} // namespace
|
|
|
|
#endif
|