#pragma once #include // enable_if #include // CHAR_BIT #include // size_t BEGIN_AH_NAMESPACE namespace detail { template std::enable_if_t<(Bits_out <= 2 * Bits_in) && (Bits_out > Bits_in), T_out> increaseBitDepthImpl(T_in in) { constexpr size_t leftShift = Bits_out - Bits_in; constexpr size_t rightShift = Bits_in - leftShift; return (static_cast(in) << leftShift) | (in >> rightShift); } template std::enable_if_t<(Bits_out <= Bits_in), T_out> increaseBitDepthImpl(T_in in) { constexpr size_t rightShift = Bits_in - Bits_out; return static_cast(in >> rightShift); } template std::enable_if_t<(Bits_out > 2 * Bits_in), T_out> increaseBitDepthImpl(T_in in) { constexpr size_t leftShift = Bits_out - Bits_in; return (static_cast(in) << leftShift) | increaseBitDepthImpl(in); } } // namespace detail /// @addtogroup AH_Math /// @{ /** * @brief Increase the bit depth of the given value from `Bits_in` bits wide * to `Bits_out` bits wide, (approximately) evenly distributing the * error across the entire range, such that the error for each element * is between -1 and +1. * * @see @ref increaseBitDepthMiddle * * For example, converting 3-bit numbers to 7-bit numbers would result in the * following: * * | in (dec) | in (bin) | out (bin) | out (dec) | exact | error | * |:--------:|:--------:|:---------:|:---------:|:------:|:-----:| * | 0 | 000 | 000'0000 | 0 | 0.00 | +0.00 | * | 1 | 001 | 001'0010 | 18 | 18.14 | +0.14 | * | 2 | 010 | 010'0100 | 36 | 36.29 | +0.29 | * | 3 | 011 | 011'0110 | 54 | 54.43 | +0.43 | * | 4 | 100 | 100'1001 | 73 | 72.57 | -0.43 | * | 5 | 101 | 101'1011 | 91 | 90.71 | -0.29 | * | 6 | 110 | 110'1101 | 109 | 108.86 | -0.14 | * | 7 | 111 | 111'1111 | 127 | 127.00 | +0.00 | * * The following is a comparison to the @ref increaseBitDepthMiddle function. * * @image html increase-bit-depth.svg * * @tparam Bits_out * The number of bits of the output range. * @tparam Bits_in * The number of bits of the input range. * @tparam T_out * The type of the output (return type). * @tparam T_in * The type of the input. * @param in * The value to scale up. * @return The scaled up value. */ template T_out increaseBitDepth(T_in in) { static_assert(Bits_in <= sizeof(T_in) * CHAR_BIT, "Error: Bits_in > bits(T_in)"); static_assert(Bits_out <= sizeof(T_out) * CHAR_BIT, "Error: Bits_out > bits(T_out)"); return detail::increaseBitDepthImpl(in); } /** * @brief Increase the bit depth of the given value from `Bits_in` bits wide * to `Bits_out` bits wide, while ensuring that the middle of the input * range maps exactly to the middle of the output range, i.e. * @f$ 2^{\texttt{Bits\_in} - 1} @f$ maps to * @f$ 2^{\texttt{Bits\_out} - 1} @f$. * * @see @ref increaseBitDepth * * For example, converting 3-bit numbers to 7-bit numbers would result in the * following: * * | in (dec) | in (bin) | out (bin) | out (dec) | exact | error | * |:--------:|:--------:|:---------:|:---------:|:------:|:-----:| * | 0 | 000 | 000'0000 | 0 | 0.00 | +0.00 | * | 1 | 001 | 001'0000 | 16 | 18.14 | -2.14 | * | 2 | 010 | 010'0000 | 32 | 36.29 | -4.29 | * | 3 | 011 | 011'0000 | 48 | 54.43 | -6.43 | * | 4 | 100 | 100'0000 | 64 | 72.57 | -8.57 | * | 5 | 101 | 101'0101 | 85 | 90.71 | -5.71 | * | 6 | 110 | 110'1010 | 106 | 108.86 | -2.86 | * | 7 | 111 | 111'1111 | 127 | 127.00 | +0.00 | * * The following is a comparison to the @ref increaseBitDepth function. * * @image html increase-bit-depth.svg * * @tparam Bits_out * The number of bits of the output range. * @tparam Bits_in * The number of bits of the input range. * @tparam T_out * The type of the output (return type). * @tparam T_in * The type of the input. * @param in * The value to scale up. * @return The scaled up value. */ template T_out increaseBitDepthMiddle(T_in in) { static_assert(Bits_in <= sizeof(T_in) * CHAR_BIT, "Error: Bits_in > bits(T_in)"); static_assert(Bits_out <= sizeof(T_out) * CHAR_BIT, "Error: Bits_out > bits(T_out)"); constexpr size_t leftShift = Bits_out - Bits_in; T_in half = T_in {1} << (Bits_in - 1); T_out out = static_cast(in) << leftShift; if (in > half) { T_in repeat = in & (half - 1); out |= increaseBitDepth(repeat); } return out; } /// @} END_AH_NAMESPACE