#include #include "grotto/dyadic_lut.hpp" #include #include namespace { int ref_clz(std::int16_t raw) { const auto bits = static_cast(raw); if (bits == 0) return 16; return __builtin_clz(static_cast(bits)) - (32 - 16); } int ref_clrsb(std::int16_t raw) { const auto bits = static_cast(raw); const bool neg = (bits & 0x8000u) != 0; int count = 0; for (int b = 14; b >= 0; --b) { const bool bit = ((bits >> b) & 1u) != 0; if (bit != neg) break; ++count; } return count; } int ref_ilogb(std::int16_t raw, unsigned k) { if (raw == 0) return 0; unsigned mag; if (raw == std::numeric_limits::min()) mag = 1u << 15; else mag = static_cast(raw < 0 ? -raw : raw); int log = 0; while (mag > 1) { mag >>= 1; ++log; } return log - static_cast(k); } int ref_ilog10(std::int64_t raw, unsigned k) { if (raw == 0) return 0; using u128 = unsigned __int128; u128 mag; if (raw == std::numeric_limits::min()) mag = u128{1} << 63; else mag = static_cast(raw < 0 ? -raw : raw); int e = -static_cast(k) - 2; for (;;) { bool ge = false; if (e >= 0) { u128 decade = 1; for (int i = 0; i < e; ++i) decade *= 10; ge = mag >= (decade << k); } else { u128 decade = 1; for (int i = 0; i < -e; ++i) decade *= 10; const u128 scale = u128{1} << k; u128 threshold = scale / decade; if (scale % decade != 0) ++threshold; ge = mag >= threshold; } if (!ge) return e - 1; ++e; if (e > 40) return 40; } } std::int64_t enc(std::int64_t units, unsigned k) { return units << k; } } // namespace TEST(DyadicLut, SignBundleInt16) { const auto positive = grotto::make_positive_lut(); const auto negative = grotto::make_negative_lut(); const auto nonnegative = grotto::make_nonnegative_lut(); const auto nonpositive = grotto::make_nonpositive_lut(); const auto zero = grotto::make_zero_lut(); const auto nonzero = grotto::make_nonzero_lut(); const auto signum = grotto::make_signum_lut(); EXPECT_EQ(positive.parts(), 2u); EXPECT_EQ(signum.parts(), 3u); EXPECT_EQ(zero.parts(), 3u); for (std::int32_t raw = -32768; raw <= 32767; ++raw) { const auto x = static_cast(raw); EXPECT_EQ(positive(x), x > 0 ? 1 : 0); EXPECT_EQ(negative(x), x < 0 ? 1 : 0); EXPECT_EQ(nonnegative(x), x >= 0 ? 1 : 0); EXPECT_EQ(nonpositive(x), x <= 0 ? 1 : 0); EXPECT_EQ(zero(x), x == 0 ? 1 : 0); EXPECT_EQ(nonzero(x), x != 0 ? 1 : 0); const int sgn = x < 0 ? -1 : (x > 0 ? 1 : 0); EXPECT_EQ(signum(x), sgn); } const auto scaled = grotto::make_positive_lut(4); EXPECT_EQ(scaled(std::int16_t{1}), 16); EXPECT_EQ(scaled(std::int16_t{0}), 0); EXPECT_EQ(grotto::make_signum_lut(4)(std::int16_t{-3}), -16); } TEST(DyadicLut, ClzAndClrsbInt16) { const auto clz = grotto::make_clz_lut(); const auto clrsb = grotto::make_clrsb_lut(); EXPECT_EQ(clz.parts(), 17u); for (std::int32_t raw = -32768; raw <= 32767; ++raw) { const auto x = static_cast(raw); EXPECT_EQ(clz(x), ref_clz(x)) << raw; EXPECT_EQ(clrsb(x), ref_clrsb(x)) << raw; } const auto scaled = grotto::make_clz_lut(3); EXPECT_EQ(scaled(std::int16_t{1}), ref_clz(1) << 3); } TEST(DyadicLut, IntegerLogsInt16) { for (unsigned k : {0u, 4u}) { const auto lg = grotto::make_ilogb_lut(k); const auto log10 = grotto::make_ilog10_lut(k); EXPECT_EQ(lg(std::int16_t{0}), grotto::ilog_of_zero); EXPECT_EQ(log10(std::int16_t{0}), grotto::ilog_of_zero); for (std::int32_t raw = -32768; raw <= 32767; ++raw) { if (raw == 0) continue; const auto x = static_cast(raw); EXPECT_EQ(lg(x), enc(ref_ilogb(x, k), k)) << raw << " k=" << k; EXPECT_EQ(log10(x), enc(ref_ilog10(raw, k), k)) << raw << " k=" << k; } } EXPECT_EQ(grotto::make_ilogb_lut()(std::int16_t{1}), 0); EXPECT_EQ(grotto::make_ilogb_lut()(std::int16_t{2}), 1); EXPECT_EQ(grotto::make_ilog10_lut()(std::int16_t{9}), 0); EXPECT_EQ(grotto::make_ilog10_lut()(std::int16_t{10}), 1); EXPECT_EQ(grotto::make_ilog10_lut(4)(std::int16_t{1}), enc(-2, 4)); } TEST(DyadicLut, MostSignificantBits) { for (unsigned i = 0; i < grotto::msb_bit_limit && i < 16; ++i) { const auto lut = grotto::make_msb_lut(i); EXPECT_EQ(lut.parts(), std::size_t{1} << (i + 1)) << i; const int shift = 15 - static_cast(i); for (std::int32_t raw = -32768; raw <= 32767; ++raw) { const auto x = static_cast(raw); const int bit = (static_cast(x) >> shift) & 1; EXPECT_EQ(lut(x), bit) << raw << " i=" << i; } } EXPECT_THROW(grotto::make_msb_lut(grotto::msb_bit_limit), std::invalid_argument); const auto scaled = grotto::make_msb_lut(0, 4); EXPECT_EQ(scaled(std::int16_t{-1}), 16); EXPECT_EQ(scaled(std::int16_t{1}), 0); } TEST(DyadicLut, Int64Edges) { const auto clz = grotto::make_clz_lut(); EXPECT_EQ(clz(std::int64_t{0}), 64); EXPECT_EQ(clz(std::int64_t{-1}), 0); EXPECT_EQ(clz(std::int64_t{1}), 63); EXPECT_EQ(clz(std::numeric_limits::min()), 0); EXPECT_EQ(clz(std::int64_t{1} << 62), 1); const auto clrsb = grotto::make_clrsb_lut(); EXPECT_EQ(clrsb(std::int64_t{0}), 63); EXPECT_EQ(clrsb(std::int64_t{-1}), 63); EXPECT_EQ(clrsb(std::int64_t{1}), 62); EXPECT_EQ(clrsb(std::numeric_limits::min()), 0); const auto lg = grotto::make_ilogb_lut(16); EXPECT_EQ(lg(std::int64_t{1} << 16), 0); EXPECT_EQ(lg(std::int64_t{1} << 17), enc(1, 16)); EXPECT_EQ(lg(std::numeric_limits::min()), enc(63 - 16, 16)); const auto bit = grotto::make_msb_lut(0); EXPECT_EQ(bit.parts(), 2u); EXPECT_EQ(bit(std::int64_t{-5}), 1); EXPECT_EQ(bit(std::int64_t{5}), 0); }