celestia/test/unit/charconv_compat_test.cpp

325 lines
14 KiB
C++

#include <cmath>
#include <cstddef>
#include <iostream>
#include <limits>
#include <string>
#include <system_error>
#include <celcompat/charconv.h>
#include <catch.hpp>
namespace celcompat = celestia::compat;
template<typename T>
struct TestCase
{
const char* source;
std::size_t size;
T expected;
std::ptrdiff_t length;
};
TEMPLATE_TEST_CASE("Floating point general format: successful", "[charconv][floating-point]", float, double, long double)
{
TestCase<TestType> examples[] = {
{ "123", 3, TestType{ 123.0 }, 3 },
{ "1234", 3, TestType{ 123.0 }, 3 },
{ "123c", 4, TestType{ 123.0 }, 3},
{ ".5", 2, TestType{ 0.5 }, 2 },
{ "108.", 4, TestType{ 108.0 }, 4 },
{ "108.5", 4, TestType{ 108.0 }, 4},
{ "23.5", 4, TestType{ 23.5 }, 4 },
{ "132e", 4, TestType{ 132 }, 3 },
{ "14e2", 4, TestType{ 1400.0 }, 4 },
{ "92.e1", 5, TestType{ 920.0 }, 5 },
{ "1.4e2", 5, TestType{ 140.0 }, 5 },
{ "14E+2", 5, TestType{ 1400.0 }, 5 },
{ ".5e3", 4, TestType{ 500.0 }, 4 },
{ "92.e+1", 6, TestType{ 920.0 }, 6 },
{ "1.4E+2", 6, TestType{ 140.0 }, 6 },
{ "5e-1", 4, TestType{ 0.5 }, 4 },
{ "5.e-1", 5, TestType{ 0.5 }, 5 },
{ "2.5e-1", 6, TestType{ 0.25 }, 6 },
{ "-123", 4, TestType{ -123.0 }, 4 },
{ "-123", 3, TestType{ -12.0 }, 3 },
{ "-108.", 5, TestType{ -108.0 }, 5 },
{ "-23.5", 5, TestType{ -23.5 }, 5 },
{ "-14e2", 5, TestType{ -1400.0 }, 5 },
{ "-14e25", 5, TestType{ -1400.0 }, 5 },
{ "-92.e1", 6, TestType{ -920.0 }, 6 },
{ "-1.4E2", 6, TestType{ -140.0 }, 6 },
{ "-14e+2", 6, TestType{ -1400.0 }, 6 },
{ "-92.e+1", 7, TestType{ -920.0 }, 7 },
{ "-1.4E+2", 7, TestType{ -140.0 }, 7 },
{ "-5e-1", 5, TestType{ -0.5 }, 5 },
{ "-5.E-1", 6, TestType{ -0.5 }, 6 },
{ "-2.5e-1", 7, TestType{ -0.25 }, 7 },
{ "inf", 3, std::numeric_limits<TestType>::infinity(), 3 },
{ "Inf", 3, std::numeric_limits<TestType>::infinity(), 3 },
{ "INF", 3, std::numeric_limits<TestType>::infinity(), 3 },
{ "infi", 4, std::numeric_limits<TestType>::infinity(), 3 },
{ "Infi", 4, std::numeric_limits<TestType>::infinity(), 3 },
{ "INFI", 4, std::numeric_limits<TestType>::infinity(), 3 },
{ "infinity", 8, std::numeric_limits<TestType>::infinity(), 8 },
{ "Infinity", 8, std::numeric_limits<TestType>::infinity(), 8 },
{ "INFINITY", 8, std::numeric_limits<TestType>::infinity(), 8 },
{ "-inf", 4, -std::numeric_limits<TestType>::infinity(), 4 },
{ "-Inf", 4, -std::numeric_limits<TestType>::infinity(), 4 },
{ "-INF", 4, -std::numeric_limits<TestType>::infinity(), 4 },
{ "-infi", 5, -std::numeric_limits<TestType>::infinity(), 4 },
{ "-Infi", 5, -std::numeric_limits<TestType>::infinity(), 4 },
{ "-INFI", 5, -std::numeric_limits<TestType>::infinity(), 4 },
{ "-infinity", 9, -std::numeric_limits<TestType>::infinity(), 9 },
{ "-Infinity", 9, -std::numeric_limits<TestType>::infinity(), 9 },
{ "-INFINITY", 9, -std::numeric_limits<TestType>::infinity(), 9 },
};
for (const auto& example : examples)
{
TestType actual;
auto result = celcompat::from_chars(example.source, example.source + example.size, actual, celcompat::chars_format::general);
REQUIRE(result.ec == std::errc{});
REQUIRE(actual == example.expected);
auto length = static_cast<std::size_t>(result.ptr - example.source);
REQUIRE(length == example.length);
}
}
TEMPLATE_TEST_CASE("Floating point general format: negative zero", "[charconv][floating-point]", float, double, long double)
{
TestCase<TestType> examples[] = {
{ "-0", 2, TestType{ -0.0 }, 2 },
{ "-0.", 3, TestType{ -0.0 }, 3 },
{ "-0.0", 4, TestType{ -0.0 }, 4 },
};
for (const auto& example : examples)
{
TestType actual;
auto result = celcompat::from_chars(example.source, example.source + example.size, actual, celcompat::chars_format::general);
REQUIRE(result.ec == std::errc{});
REQUIRE(actual == -0.0f);
float signum = std::copysign(1.0f, actual);
REQUIRE(signum == -1.0f);
auto length = static_cast<std::size_t>(result.ptr - example.source);
REQUIRE(length == example.length);
}
}
TEMPLATE_TEST_CASE("Floating point general format: NaN", "[charconv][floating-point]", float, double, long double)
{
TestCase<TestType> examples[] = {
{ "nan", 3, std::numeric_limits<TestType>::quiet_NaN(), 3 },
{ "NaN", 3, std::numeric_limits<TestType>::quiet_NaN(), 3 },
{ "NAN", 3, std::numeric_limits<TestType>::quiet_NaN(), 3 },
{ "naN()", 5, std::numeric_limits<TestType>::quiet_NaN(), 5 },
{ "nAn(abc)", 8, std::numeric_limits<TestType>::quiet_NaN(), 8 },
{ "nan(abcd", 8, std::numeric_limits<TestType>::quiet_NaN(), 3 },
{ "NaN(a^c)", 8, std::numeric_limits<TestType>::quiet_NaN(), 3 },
{ "-naN()", 6, std::numeric_limits<TestType>::quiet_NaN(), 6 },
{ "-nAn(abc)", 9, std::numeric_limits<TestType>::quiet_NaN(), 9 },
{ "-nan(abcd", 9, std::numeric_limits<TestType>::quiet_NaN(), 4 },
{ "-NaN(a^c)", 9, std::numeric_limits<TestType>::quiet_NaN(), 4 },
};
for (const auto& example : examples)
{
TestType actual;
auto result = celcompat::from_chars(example.source, example.source + example.size, actual, celcompat::chars_format::general);
REQUIRE(result.ec == std::errc{});
REQUIRE(std::isnan(actual));
auto length = static_cast<std::size_t>(result.ptr - example.source);
REQUIRE(length == example.length);
}
}
TEMPLATE_TEST_CASE("Floating point fixed format: successful", "[charconv][floating-point]", float, double, long double)
{
TestCase<TestType> examples[] = {
{ "123", 3, TestType{ 123.0 }, 3 },
{ "1234", 3, TestType{ 123.0 }, 3 },
{ "123c", 4, TestType{ 123.0 }, 3},
{ ".5", 2, TestType{ 0.5 }, 2 },
{ "108.", 4, TestType{ 108.0 }, 4 },
{ "108.5", 4, TestType{ 108.0 }, 4},
{ "23.5", 4, TestType{ 23.5 }, 4 },
{ "14e2", 4, TestType{ 14.0 }, 2 },
{ ".5e3", 4, TestType{ 0.5 }, 2 },
{ "92.e1", 5, TestType{ 92.0 }, 3 },
{ "1.5E2", 5, TestType{ 1.5 }, 3 },
{ "14e+2", 5, TestType{ 14.0 }, 2 },
{ "92.e+1", 6, TestType{ 92.0 }, 3 },
{ "1.5E+2", 6, TestType{ 1.5 }, 3 },
{ "5e-1", 4, TestType{ 5.0 }, 1 },
{ "5.e-1", 5, TestType{ 5.0 }, 2 },
{ "2.5E-1", 6, TestType{ 2.5 }, 3 },
{ "-123", 4, TestType{ -123.0 }, 4 },
{ "-123", 3, TestType{ -12.0 }, 3 },
{ "-108.", 5, TestType{ -108.0 }, 5 },
{ "-23.5", 5, TestType{ -23.5 }, 5 },
{ "-14e2", 5, TestType{ -14.0 }, 3 },
{ "-92.e1", 6, TestType{ -92.0 }, 4 },
{ "-1.5E2", 6, TestType{ -1.5 }, 4 },
{ "-14e+2", 6, TestType{ -14.0 }, 3 },
{ "-92.e+1", 7, TestType{ -92.0 }, 4 },
{ "-1.5e+2", 7, TestType{ -1.5 }, 4 },
{ "-5e-1", 5, TestType{ -5.0 }, 2 },
{ "-5.e-1", 6, TestType{ -5.0 }, 3 },
{ "-2.5e-1", 7, TestType{ -2.5 }, 4 },
{ "inf", 3, std::numeric_limits<TestType>::infinity(), 3 },
{ "Inf", 3, std::numeric_limits<TestType>::infinity(), 3 },
{ "INF", 3, std::numeric_limits<TestType>::infinity(), 3 },
{ "infi", 4, std::numeric_limits<TestType>::infinity(), 3 },
{ "Infi", 4, std::numeric_limits<TestType>::infinity(), 3 },
{ "INFI", 4, std::numeric_limits<TestType>::infinity(), 3 },
{ "infinity", 8, std::numeric_limits<TestType>::infinity(), 8 },
{ "Infinity", 8, std::numeric_limits<TestType>::infinity(), 8 },
{ "INFINITY", 8, std::numeric_limits<TestType>::infinity(), 8 },
{ "-inf", 4, -std::numeric_limits<TestType>::infinity(), 4 },
{ "-Inf", 4, -std::numeric_limits<TestType>::infinity(), 4 },
{ "-INF", 4, -std::numeric_limits<TestType>::infinity(), 4 },
{ "-infi", 5, -std::numeric_limits<TestType>::infinity(), 4 },
{ "-Infi", 5, -std::numeric_limits<TestType>::infinity(), 4 },
{ "-INFI", 5, -std::numeric_limits<TestType>::infinity(), 4 },
{ "-infinity", 9, -std::numeric_limits<TestType>::infinity(), 9 },
{ "-Infinity", 9, -std::numeric_limits<TestType>::infinity(), 9 },
{ "-INFINITY", 9, -std::numeric_limits<TestType>::infinity(), 9 },
};
for (const auto& example : examples)
{
TestType actual;
auto result = celcompat::from_chars(example.source, example.source + example.size, actual, celcompat::chars_format::fixed);
REQUIRE(result.ec == std::errc{});
REQUIRE(actual == example.expected);
auto length = static_cast<std::size_t>(result.ptr - example.source);
REQUIRE(length == example.length);
}
}
TEMPLATE_TEST_CASE("Floating point scientific format: successful", "[charconv][floating-point]", float, double, long double)
{
TestCase<TestType> examples[] = {
{ "14e2", 4, TestType { 1400.0 }, 4 },
{ "92.E1", 5, TestType{ 920.0 }, 5 },
{ "1.4e2", 5, TestType{ 140.0 }, 5 },
{ "14e+2", 5, TestType{ 1400.0 }, 5 },
{ "92.e+1", 6, TestType{ 920.0 }, 6 },
{ "1.4e+2", 6, TestType{ 140.0 }, 6 },
{ "5e-1", 4, TestType{ 0.5 }, 4 },
{ "5.e-1", 5, TestType{ 0.5 }, 5 },
{ "2.5e-1", 6, TestType{ 0.25 }, 6 },
{ "-14e2", 5, TestType{ -1400.0 }, 5 },
{ ".5e3", 4, TestType{ 500.0 }, 4 },
{ "-14e25", 5, TestType{ -1400.0 }, 5 },
{ "-92.e1", 6, TestType{ -920.0 }, 6 },
{ "-1.4E2", 6, TestType{ -140.0 }, 6 },
{ "-14e+2", 6, TestType{ -1400.0 }, 6 },
{ "-92.e+1", 7, TestType{ -920.0 }, 7 },
{ "-1.4E+2", 7, TestType{ -140.0 }, 7 },
{ "-5e-1", 5, TestType{ -0.5 }, 5 },
{ "-5.E-1", 6, TestType{ -0.5 }, 6 },
{ "-2.5E-1", 7, TestType{ -0.25 }, 7 },
{ "inf", 3, std::numeric_limits<TestType>::infinity(), 3 },
{ "Inf", 3, std::numeric_limits<TestType>::infinity(), 3 },
{ "INF", 3, std::numeric_limits<TestType>::infinity(), 3 },
{ "infi", 4, std::numeric_limits<TestType>::infinity(), 3 },
{ "Infi", 4, std::numeric_limits<TestType>::infinity(), 3 },
{ "INFI", 4, std::numeric_limits<TestType>::infinity(), 3 },
{ "infinity", 8, std::numeric_limits<TestType>::infinity(), 8 },
{ "Infinity", 8, std::numeric_limits<TestType>::infinity(), 8 },
{ "INFINITY", 8, std::numeric_limits<TestType>::infinity(), 8 },
{ "-inf", 4, -std::numeric_limits<TestType>::infinity(), 4 },
{ "-Inf", 4, -std::numeric_limits<TestType>::infinity(), 4 },
{ "-INF", 4, -std::numeric_limits<TestType>::infinity(), 4 },
{ "-infi", 5, -std::numeric_limits<TestType>::infinity(), 4 },
{ "-Infi", 5, -std::numeric_limits<TestType>::infinity(), 4 },
{ "-INFI", 5, -std::numeric_limits<TestType>::infinity(), 4 },
{ "-infinity", 9, -std::numeric_limits<TestType>::infinity(), 9 },
{ "-Infinity", 9, -std::numeric_limits<TestType>::infinity(), 9 },
{ "-INFINITY", 9, -std::numeric_limits<TestType>::infinity(), 9 },
};
for (const auto& example : examples)
{
TestType actual;
auto result = celcompat::from_chars(example.source, example.source + example.size, actual, celcompat::chars_format::scientific);
REQUIRE(result.ec == std::errc{});
REQUIRE(actual == example.expected);
auto length = static_cast<std::size_t>(result.ptr - example.source);
REQUIRE(length == example.length);
}
}
TEMPLATE_TEST_CASE("Floating point scientific format: missing exponential", "[charconv][floating-point]", float, double, long double)
{
TestCase<TestType> examples[] = {
{ "123", 3, TestType{ 123.0 }, 3 },
{ "1234", 3, TestType{ 123.0 }, 3 },
{ "123c", 4, TestType{ 123.0 }, 3},
{ ".5", 2, TestType{ 0.5 }, 2 },
{ "108.", 4, TestType{ 108.0 }, 4 },
{ "108.5", 4, TestType{ 108.0 }, 4},
{ "23.5", 4, TestType{ 23.5 }, 4 },
{ "-123", 4, TestType{ -123.0 }, 4 },
{ "-123", 3, TestType{ -12.0 }, 3 },
{ "-108.", 5, TestType{ -108.0 }, 5 },
{ "-23.5", 5, TestType{ -23.5 }, 5 },
};
for (const auto& example : examples)
{
TestType actual;
auto result = celcompat::from_chars(example.source, example.source + example.size, actual, celcompat::chars_format::scientific);
REQUIRE(result.ec == std::errc::invalid_argument);
auto length = static_cast<std::size_t>(result.ptr - example.source);
REQUIRE(length == 0);
}
}
TEMPLATE_TEST_CASE("Hexadecimal floating point", "[charconv][floating-point]", float, double, long double)
{
TestCase<TestType> examples[] = {
{ "1b", 2, TestType{ 27 }, 2 },
{ ".8", 2, TestType{ 0.5 }, 2 },
{ "1.c", 3, TestType{ 1.75 }, 3 },
{ "1.3eP10", 7, TestType{ 1272 }, 7 },
{ "1.3Ep+10", 8, TestType{ 1272 }, 8 },
};
for (const auto& example : examples)
{
TestType actual;
auto result = celcompat::from_chars(example.source, example.source + example.size, actual, celcompat::chars_format::hex);
REQUIRE(result.ec == std::errc{});
REQUIRE(actual == example.expected);
auto length = static_cast<std::size_t>(result.ptr - example.source);
REQUIRE(length == example.length);
}
}
TEMPLATE_TEST_CASE("Floating point format failures", "[charconv][floating-point]", float, double, long double)
{
TestCase<TestType> examples[] = {
{ " 0.5", 5, TestType{ }, 0 },
{ ".e3", 3, TestType{ }, 0 },
{ "+23", 3, TestType{ }, 0 },
{ "N", 1, TestType{ }, 0 },
{ "NA", 2, TestType{ }, 0 },
{ "N/A", 3, TestType{ }, 0 },
{ "in", 2, TestType{ }, 0 },
{ "-N/A", 4, TestType{ }, 0 },
{ "-in", 3, TestType{ }, 0 },
};
for (const auto& example : examples)
{
TestType actual;
auto result = celcompat::from_chars(example.source, example.source + example.size, actual, celcompat::chars_format::general);
REQUIRE(result.ec == std::errc::invalid_argument);
auto length = static_cast<std::size_t>(result.ptr - example.source);
REQUIRE(length == example.length);
}
}