216 lines
6.7 KiB
C++
216 lines
6.7 KiB
C++
|
|
#include <gtest/gtest.h>
|
||
|
|
|
||
|
|
#include "grotto/dyadic_lut.hpp"
|
||
|
|
|
||
|
|
#include <cstdint>
|
||
|
|
#include <limits>
|
||
|
|
|
||
|
|
namespace
|
||
|
|
{
|
||
|
|
|
||
|
|
int ref_clz(std::int16_t raw)
|
||
|
|
{
|
||
|
|
const auto bits = static_cast<std::uint16_t>(raw);
|
||
|
|
if (bits == 0)
|
||
|
|
return 16;
|
||
|
|
return __builtin_clz(static_cast<unsigned>(bits)) - (32 - 16);
|
||
|
|
}
|
||
|
|
|
||
|
|
int ref_clrsb(std::int16_t raw)
|
||
|
|
{
|
||
|
|
const auto bits = static_cast<std::uint16_t>(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<std::int16_t>::min())
|
||
|
|
mag = 1u << 15;
|
||
|
|
else
|
||
|
|
mag = static_cast<unsigned>(raw < 0 ? -raw : raw);
|
||
|
|
int log = 0;
|
||
|
|
while (mag > 1)
|
||
|
|
{
|
||
|
|
mag >>= 1;
|
||
|
|
++log;
|
||
|
|
}
|
||
|
|
return log - static_cast<int>(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<std::int64_t>::min())
|
||
|
|
mag = u128{1} << 63;
|
||
|
|
else
|
||
|
|
mag = static_cast<u128>(raw < 0 ? -raw : raw);
|
||
|
|
int e = -static_cast<int>(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<std::int16_t>();
|
||
|
|
const auto negative = grotto::make_negative_lut<std::int16_t>();
|
||
|
|
const auto nonnegative = grotto::make_nonnegative_lut<std::int16_t>();
|
||
|
|
const auto nonpositive = grotto::make_nonpositive_lut<std::int16_t>();
|
||
|
|
const auto zero = grotto::make_zero_lut<std::int16_t>();
|
||
|
|
const auto nonzero = grotto::make_nonzero_lut<std::int16_t>();
|
||
|
|
const auto signum = grotto::make_signum_lut<std::int16_t>();
|
||
|
|
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<std::int16_t>(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<std::int16_t>(4);
|
||
|
|
EXPECT_EQ(scaled(std::int16_t{1}), 16);
|
||
|
|
EXPECT_EQ(scaled(std::int16_t{0}), 0);
|
||
|
|
EXPECT_EQ(grotto::make_signum_lut<std::int16_t>(4)(std::int16_t{-3}), -16);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(DyadicLut, ClzAndClrsbInt16)
|
||
|
|
{
|
||
|
|
const auto clz = grotto::make_clz_lut<std::int16_t>();
|
||
|
|
const auto clrsb = grotto::make_clrsb_lut<std::int16_t>();
|
||
|
|
EXPECT_EQ(clz.parts(), 17u);
|
||
|
|
for (std::int32_t raw = -32768; raw <= 32767; ++raw)
|
||
|
|
{
|
||
|
|
const auto x = static_cast<std::int16_t>(raw);
|
||
|
|
EXPECT_EQ(clz(x), ref_clz(x)) << raw;
|
||
|
|
EXPECT_EQ(clrsb(x), ref_clrsb(x)) << raw;
|
||
|
|
}
|
||
|
|
const auto scaled = grotto::make_clz_lut<std::int16_t>(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<std::int16_t>(k);
|
||
|
|
const auto log10 = grotto::make_ilog10_lut<std::int16_t>(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<std::int16_t>(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>()(std::int16_t{1}), 0);
|
||
|
|
EXPECT_EQ(grotto::make_ilogb_lut<std::int16_t>()(std::int16_t{2}), 1);
|
||
|
|
EXPECT_EQ(grotto::make_ilog10_lut<std::int16_t>()(std::int16_t{9}), 0);
|
||
|
|
EXPECT_EQ(grotto::make_ilog10_lut<std::int16_t>()(std::int16_t{10}), 1);
|
||
|
|
EXPECT_EQ(grotto::make_ilog10_lut<std::int16_t>(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<std::int16_t>(i);
|
||
|
|
EXPECT_EQ(lut.parts(), std::size_t{1} << (i + 1)) << i;
|
||
|
|
const int shift = 15 - static_cast<int>(i);
|
||
|
|
for (std::int32_t raw = -32768; raw <= 32767; ++raw)
|
||
|
|
{
|
||
|
|
const auto x = static_cast<std::int16_t>(raw);
|
||
|
|
const int bit = (static_cast<std::uint16_t>(x) >> shift) & 1;
|
||
|
|
EXPECT_EQ(lut(x), bit) << raw << " i=" << i;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
EXPECT_THROW(grotto::make_msb_lut<std::int16_t>(grotto::msb_bit_limit),
|
||
|
|
std::invalid_argument);
|
||
|
|
const auto scaled = grotto::make_msb_lut<std::int16_t>(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<std::int64_t>();
|
||
|
|
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<std::int64_t>::min()), 0);
|
||
|
|
EXPECT_EQ(clz(std::int64_t{1} << 62), 1);
|
||
|
|
|
||
|
|
const auto clrsb = grotto::make_clrsb_lut<std::int64_t>();
|
||
|
|
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<std::int64_t>::min()), 0);
|
||
|
|
|
||
|
|
const auto lg = grotto::make_ilogb_lut<std::int64_t>(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<std::int64_t>::min()), enc(63 - 16, 16));
|
||
|
|
|
||
|
|
const auto bit = grotto::make_msb_lut<std::int64_t>(0);
|
||
|
|
EXPECT_EQ(bit.parts(), 2u);
|
||
|
|
EXPECT_EQ(bit(std::int64_t{-5}), 1);
|
||
|
|
EXPECT_EQ(bit(std::int64_t{5}), 0);
|
||
|
|
}
|