#include #include #include "grotto/exact_steps.hpp" #include #include #include namespace { int ref_bit_width(std::uint16_t bits) { if (bits == 0) return 0; return 32 - __builtin_clz(static_cast(bits)); } int ref_countl_one(std::uint16_t bits) { int n = 0; for (int b = 15; b >= 0; --b) { if (((bits >> b) & 1u) == 0) break; ++n; } return n; } std::uint16_t ref_bit_floor(std::uint16_t bits) { if (bits == 0) return 0; return std::uint16_t{1} << (ref_bit_width(bits) - 1); } std::uint32_t ref_bit_ceil(std::uint16_t bits) { if (bits <= 1) return 1; if ((bits & (bits - 1u)) == 0) return bits; return std::uint32_t{1} << ref_bit_width(bits); } int ref_ceil_log(std::int16_t raw, unsigned k, int group) { const long double x = std::ldexp(static_cast(raw < 0 ? -raw : raw), -static_cast(k)); return static_cast(std::ceil(std::log2(static_cast(x)) / group - 1e-15)); } int ref_logstar(std::int16_t raw, unsigned k) { long double x = std::ldexp(static_cast(raw), -static_cast(k)); int n = 0; while (x > 1.0L) { x = std::log2(static_cast(x)); ++n; if (n > 8) break; } return n; } int ref_length(std::int16_t raw, unsigned k, int base) { const long double x = std::ldexp(static_cast(raw < 0 ? -raw : raw), -static_cast(k)); const auto n = static_cast(std::floor(static_cast(x))); if (n <= 0) return 1; int digits = 0; auto m = n; while (m > 0) { m /= base; ++digits; } return digits; } } // namespace TEST(ExactSteps, WordBitsMatchTheUnsignedPattern) { for (int raw = -32768; raw <= 32767; ++raw) { const auto bits = static_cast(raw); const auto value = static_cast(static_cast(raw)); EXPECT_EQ(grotto::eval_bit_width(value, 0), ref_bit_width(bits)); EXPECT_EQ(grotto::eval_countl_one(value, 0), ref_countl_one(bits)); EXPECT_EQ(grotto::eval_has_single_bit(value, 0), ((bits != 0 && (bits & (bits - 1u)) == 0) ? 1 : 0)); EXPECT_EQ(grotto::eval_bit_floor(value), ref_bit_floor(bits)); EXPECT_EQ(grotto::eval_bit_ceil(value), ref_bit_ceil(bits)); } } TEST(ExactSteps, IntegerLogsAndLogstar) { for (unsigned k : {0u, 4u}) { for (int raw = 1; raw <= 32767; raw += (k == 0 ? 17 : 3)) { const auto value = static_cast(raw); EXPECT_EQ(grotto::eval_ilog16(value, k) >> k, ref_ceil_log(static_cast(raw), k, 4)) << raw << " k=" << k; EXPECT_EQ(grotto::eval_ilog256(value, k) >> k, ref_ceil_log(static_cast(raw), k, 8)) << raw; EXPECT_EQ(grotto::eval_logstar(value, k) >> k, ref_logstar(static_cast(raw), k)) << raw; } } EXPECT_EQ(grotto::eval_ilog16(0, 0), grotto::ilog_of_zero); EXPECT_EQ(grotto::eval_logstar(-4, 0), grotto::ilog_of_zero); EXPECT_EQ(grotto::eval_logstar(1, 0), 0); EXPECT_EQ(grotto::eval_logstar(2, 0), 1); EXPECT_EQ(grotto::eval_logstar(4, 0), 2); EXPECT_EQ(grotto::eval_logstar(16, 0), 3); } TEST(ExactSteps, DecimalFloorCeilAndLengths) { for (unsigned k : {0u, 4u}) { for (int raw = -4000; raw <= 4000; raw += 3) { const long double x = std::ldexp(static_cast(raw), -static_cast(k)); const auto floor_raw = static_cast(std::floor(static_cast(x)) * std::ldexp(1.0, static_cast(k))); const auto ceil_raw = static_cast(std::ceil(static_cast(x)) * std::ldexp(1.0, static_cast(k))); EXPECT_EQ(grotto::eval_dec_floor(raw, k), floor_raw) << raw; EXPECT_EQ(grotto::eval_dec_ceil(raw, k), ceil_raw) << raw; const auto width = static_cast(raw); EXPECT_EQ(grotto::eval_dec_width(raw, k) >> k, ref_length(width, k, 10)); EXPECT_EQ(grotto::eval_oct_width(raw, k) >> k, ref_length(width, k, 8)); EXPECT_EQ(grotto::eval_b64_width(raw, k) >> k, ref_length(width, k, 64)); const auto digits = std::llabs(static_cast(std::floor(std::fabs(static_cast(x))))); EXPECT_EQ(grotto::eval_has_single_digit(raw, k) >> k, digits <= 9 ? 1 : 0); } } } TEST(ExactSteps, AngleScaleIsTheLinearFactor) { const unsigned k = 16; const std::int64_t deg = 180ll << k; const auto rad = grotto::eval_deg2rad(deg, k); const long double want = 180.0L * 3.14159265358979323846L / 180.0L * std::ldexp(1.0L, k); EXPECT_LE(std::fabsl(static_cast(rad) - want), 64.0L); const auto back = grotto::eval_rad2deg(rad, k); EXPECT_LE(std::llabs(back - deg), 4096); } TEST(ExactSteps, StepLutAgreesWithTheScalarOnInt8) { const auto lut = grotto::make_exact_step_lut(0, [](std::int64_t raw) { return grotto::eval_bit_width(raw, 0); }); for (int raw = -128; raw <= 127; ++raw) EXPECT_EQ(lut(static_cast(raw)), grotto::eval_bit_width(raw, 0)); EXPECT_THROW(grotto::make_exact_step_lut(0, [](std::int64_t) { return 0; }), std::invalid_argument); }