Document the new DPF surfaces in one command set, and test the field, half-tree, and multipoint edges.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Ryan Henry 2026-09-24 23:18:10 -06:00
parent 0d8a5a8131
commit 0dff6df8ed
250 changed files with 12199 additions and 1981 deletions

View file

@ -34,6 +34,8 @@ long double truth_of(grotto::reduced which, long double x)
case grotto::reduced::inv: return 1.0L / x;
case grotto::reduced::rsqrt: return 1.0L / std::sqrt(x);
case grotto::reduced::invsq: return 1.0L / (x * x);
case grotto::reduced::expm1: return expm1l(x);
case grotto::reduced::log1p: return log1pl(x);
}
return 0;
}
@ -64,6 +66,8 @@ const char * name_of(grotto::reduced which)
case grotto::reduced::inv: return "inv";
case grotto::reduced::rsqrt: return "rsqrt";
case grotto::reduced::invsq: return "invsq";
case grotto::reduced::expm1: return "expm1";
case grotto::reduced::log1p: return "log1p";
}
return "?";
}
@ -244,3 +248,99 @@ TEST(RangeLut, RejectsPolesAndNonPositiveLogarithms)
EXPECT_THROW(grotto::eval_reduced(grotto::reduced::csch, 16, 0), std::domain_error);
EXPECT_THROW(grotto::eval_reduced(grotto::reduced::ln, 7, 32), std::invalid_argument);
}
TEST(RangeLut, TanAndSecPolesThrow)
{
const unsigned k = 16;
auto pole_of = [&](grotto::reduced which) {
const std::int64_t lo = raw_of(-8.0L, k);
const std::int64_t hi = raw_of(8.0L, k);
for (std::int64_t raw = lo; raw <= hi; ++raw)
{
try
{
(void)grotto::eval_reduced(which, k, raw);
}
catch (const std::domain_error &)
{
return raw;
}
}
return std::int64_t{0};
};
for (auto which : {grotto::reduced::tan, grotto::reduced::sec})
{
const std::int64_t raw = pole_of(which);
ASSERT_NE(raw, 0) << name_of(which);
EXPECT_THROW(grotto::eval_reduced(which, k, raw), std::domain_error)
<< name_of(which) << " raw=" << raw;
if (raw > raw_of(-8.0L, k))
{
EXPECT_NO_THROW(grotto::eval_reduced(which, k, raw - 1))
<< name_of(which);
}
}
}
TEST(RangeLut, Exp10RejectsAnIntegerPowerPast18)
{
const unsigned k = 16;
int first_overflow = -1;
for (int n = 0; n <= 20; ++n)
{
try
{
(void)grotto::eval_reduced(grotto::reduced::exp10, k, raw_of(static_cast<long double>(n), k));
}
catch (const std::overflow_error &)
{
first_overflow = n;
break;
}
}
ASSERT_GT(first_overflow, 0);
ASSERT_LE(first_overflow, 19);
EXPECT_NO_THROW(grotto::eval_reduced(grotto::reduced::exp10, k,
raw_of(static_cast<long double>(first_overflow - 1), k)));
EXPECT_THROW(grotto::eval_reduced(grotto::reduced::exp10, k, raw_of(19.0L, k)),
std::overflow_error);
EXPECT_THROW(grotto::eval_reduced(grotto::reduced::exp10, k, raw_of(-19.0L, k)),
std::overflow_error);
}
TEST(RangeLut, Expm1AndLog1pTrackLibm)
{
const long double expm1_samples[] = {
-4.0L, -2.0L, -1.0L, -0.7L, -0.5L, -0.25L, -0.125L, -0.015625L,
0.0L, 0.015625L, 0.125L, 0.25L, 0.5L, 0.7L, 1.0L, 1.5L, 2.0L,
};
const long double log1p_samples[] = {
-0.75L, -0.5L, -0.25L, -0.125L, -0.015625L, 0.0L, 0.015625L,
0.125L, 0.25L, 0.5L, 1.0L, 1.5L, 3.0L, 7.5L, 16.0L, 100.0L,
};
for (unsigned k : grotto::principal_precisions)
{
expect_ulps(grotto::reduced::expm1, k, std::ldexp(1.0L, -static_cast<int>(k)), 2.0L);
expect_ulps(grotto::reduced::expm1, k, -std::ldexp(1.0L, -static_cast<int>(k)), 2.0L);
expect_ulps(grotto::reduced::log1p, k, std::ldexp(1.0L, -static_cast<int>(k)), 2.0L);
expect_ulps(grotto::reduced::log1p, k, -std::ldexp(1.0L, -static_cast<int>(k)), 2.0L);
for (long double x : expm1_samples)
expect_ulps(grotto::reduced::expm1, k, x, 20.0L);
for (long double x : log1p_samples)
expect_ulps(grotto::reduced::log1p, k, x, 8.0L);
}
}
TEST(RangeLut, Expm1AndLog1pFixpoints)
{
for (unsigned k : grotto::principal_precisions)
{
EXPECT_EQ(grotto::eval_reduced(grotto::reduced::expm1, k, 0), 0);
EXPECT_EQ(grotto::eval_reduced(grotto::reduced::log1p, k, 0), 0);
const std::int64_t neg_one = -raw_of(1.0L, k);
EXPECT_THROW(grotto::eval_reduced(grotto::reduced::log1p, k, neg_one), std::domain_error);
EXPECT_THROW(grotto::eval_reduced(grotto::reduced::log1p, k, neg_one - 1), std::domain_error);
const std::int64_t far = raw_of(-40.0L, k);
EXPECT_EQ(grotto::eval_reduced(grotto::reduced::expm1, k, far), neg_one);
}
}