/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2024, Paul Elder * * Fixed / floating point conversions */ #pragma once #include #include #include "quantized.h" namespace libcamera { namespace ipa { #ifndef __DOXYGEN__ template && std::is_floating_point_v> * = nullptr> #else template #endif constexpr R floatingToFixedPoint(T number) { static_assert(sizeof(int) >= sizeof(R)); static_assert(I + F <= sizeof(R) * 8); /* * The intermediate cast to int is needed on arm platforms to properly * cast negative values. See * https://embeddeduse.com/2013/08/25/casting-a-negative-float-to-an-unsigned-int/ */ R mask = (1 << (F + I)) - 1; R frac = static_cast(static_cast(std::round(number * (1 << F)))) & mask; return frac; } #ifndef __DOXYGEN__ template && std::is_integral_v> * = nullptr> #else template #endif constexpr R fixedToFloatingPoint(T number) { static_assert(sizeof(int) >= sizeof(T)); static_assert(I + F <= sizeof(T) * 8); if constexpr (std::is_unsigned_v) return static_cast(number) / static_cast(T{ 1 } << F); /* * Recreate the upper bits in case of a negative number by shifting the sign * bit from the fixed point to the first bit of the unsigned and then right shifting * by the same amount which keeps the sign bit in place. * This can be optimized by the compiler quite well. */ int remaining_bits = sizeof(int) * 8 - (I + F); int t = static_cast(static_cast(number) << remaining_bits) >> remaining_bits; return static_cast(t) / static_cast(1 << F); } template struct FixedPointQTraits { private: static_assert(std::is_integral_v, "FixedPointQTraits: T must be integral"); using UT = std::make_unsigned_t; static constexpr unsigned int bits = I + F; static_assert(bits <= sizeof(UT) * 8, "FixedPointQTraits: too many bits for type UT"); /* * If fixed point storage is required with more than 24 bits, consider * updating this implementation to use double-precision floating point. */ static_assert(bits <= 24, "Floating point precision may be insufficient for more than 24 bits"); static constexpr UT bitMask = bits < sizeof(UT) * 8 ? (UT{ 1 } << bits) - 1 : ~UT{ 0 }; public: using QuantizedType = UT; static constexpr UT qMin = std::is_signed_v ? -(UT{ 1 } << (bits - 1)) : 0; static constexpr UT qMax = std::is_signed_v ? (UT{ 1 } << (bits - 1)) - 1 : bitMask; static constexpr float toFloat(QuantizedType q) { return fixedToFloatingPoint(q); } static constexpr float min = fixedToFloatingPoint(static_cast(qMin)); static constexpr float max = fixedToFloatingPoint(static_cast(qMax)); static_assert(min < max, "FixedPointQTraits: Minimum must be less than maximum"); /* Conversion functions required by Quantized */ static QuantizedType fromFloat(float v) { v = std::clamp(v, min, max); return floatingToFixedPoint(v); } }; namespace details { template constexpr auto qtype() { static_assert(Bits <= 32, "Unsupported number of bits for quantized type"); if constexpr (Bits <= 8) return int8_t(); else if constexpr (Bits <= 16) return int16_t(); else if constexpr (Bits <= 32) return int32_t(); } } /* namespace details */ template using Q = Quantized())>>; template using UQ = Quantized())>>>; } /* namespace ipa */ } /* namespace libcamera */