165 lines
5.6 KiB
C++
165 lines
5.6 KiB
C++
|
|
#include <gtest/gtest.h>
|
||
|
|
#include <tuple>
|
||
|
|
|
||
|
|
#include "grotto/exact_steps.hpp"
|
||
|
|
|
||
|
|
#include <cmath>
|
||
|
|
#include <cstdint>
|
||
|
|
#include <limits>
|
||
|
|
|
||
|
|
namespace
|
||
|
|
{
|
||
|
|
|
||
|
|
int ref_bit_width(std::uint16_t bits)
|
||
|
|
{
|
||
|
|
if (bits == 0)
|
||
|
|
return 0;
|
||
|
|
return 32 - __builtin_clz(static_cast<unsigned>(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<long double>(raw < 0 ? -raw : raw), -static_cast<int>(k));
|
||
|
|
return static_cast<int>(std::ceil(std::log2(static_cast<double>(x)) / group - 1e-15));
|
||
|
|
}
|
||
|
|
|
||
|
|
int ref_logstar(std::int16_t raw, unsigned k)
|
||
|
|
{
|
||
|
|
long double x = std::ldexp(static_cast<long double>(raw), -static_cast<int>(k));
|
||
|
|
int n = 0;
|
||
|
|
while (x > 1.0L)
|
||
|
|
{
|
||
|
|
x = std::log2(static_cast<double>(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<long double>(raw < 0 ? -raw : raw), -static_cast<int>(k));
|
||
|
|
const auto n = static_cast<std::int64_t>(std::floor(static_cast<double>(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<std::uint16_t>(raw);
|
||
|
|
const auto value = static_cast<std::int64_t>(static_cast<std::int16_t>(raw));
|
||
|
|
EXPECT_EQ(grotto::eval_bit_width<std::int16_t>(value, 0), ref_bit_width(bits));
|
||
|
|
EXPECT_EQ(grotto::eval_countl_one<std::int16_t>(value, 0), ref_countl_one(bits));
|
||
|
|
EXPECT_EQ(grotto::eval_has_single_bit<std::int16_t>(value, 0),
|
||
|
|
((bits != 0 && (bits & (bits - 1u)) == 0) ? 1 : 0));
|
||
|
|
EXPECT_EQ(grotto::eval_bit_floor<std::int16_t>(value), ref_bit_floor(bits));
|
||
|
|
EXPECT_EQ(grotto::eval_bit_ceil<std::int16_t>(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<std::int64_t>(raw);
|
||
|
|
EXPECT_EQ(grotto::eval_ilog16<std::int16_t>(value, k) >> k, ref_ceil_log(static_cast<std::int16_t>(raw), k, 4))
|
||
|
|
<< raw << " k=" << k;
|
||
|
|
EXPECT_EQ(grotto::eval_ilog256<std::int16_t>(value, k) >> k, ref_ceil_log(static_cast<std::int16_t>(raw), k, 8))
|
||
|
|
<< raw;
|
||
|
|
EXPECT_EQ(grotto::eval_logstar<std::int16_t>(value, k) >> k, ref_logstar(static_cast<std::int16_t>(raw), k))
|
||
|
|
<< raw;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
EXPECT_EQ(grotto::eval_ilog16<std::int16_t>(0, 0), grotto::ilog_of_zero);
|
||
|
|
EXPECT_EQ(grotto::eval_logstar<std::int16_t>(-4, 0), grotto::ilog_of_zero);
|
||
|
|
EXPECT_EQ(grotto::eval_logstar<std::int16_t>(1, 0), 0);
|
||
|
|
EXPECT_EQ(grotto::eval_logstar<std::int16_t>(2, 0), 1);
|
||
|
|
EXPECT_EQ(grotto::eval_logstar<std::int16_t>(4, 0), 2);
|
||
|
|
EXPECT_EQ(grotto::eval_logstar<std::int16_t>(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<long double>(raw), -static_cast<int>(k));
|
||
|
|
const auto floor_raw = static_cast<std::int64_t>(std::floor(static_cast<double>(x)) * std::ldexp(1.0, static_cast<int>(k)));
|
||
|
|
const auto ceil_raw = static_cast<std::int64_t>(std::ceil(static_cast<double>(x)) * std::ldexp(1.0, static_cast<int>(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<std::int16_t>(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<long long>(std::floor(std::fabs(static_cast<double>(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<long double>(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<std::int8_t>(0, [](std::int64_t raw) {
|
||
|
|
return grotto::eval_bit_width<std::int8_t>(raw, 0);
|
||
|
|
});
|
||
|
|
for (int raw = -128; raw <= 127; ++raw)
|
||
|
|
EXPECT_EQ(lut(static_cast<std::int8_t>(raw)), grotto::eval_bit_width<std::int8_t>(raw, 0));
|
||
|
|
EXPECT_THROW(grotto::make_exact_step_lut<std::int32_t>(0, [](std::int64_t) { return 0; }), std::invalid_argument);
|
||
|
|
}
|