MikeLankamp / fpm

C++ header-only fixed-point math library
https://mikelankamp.github.io/fpm
MIT License
673 stars 85 forks source link

Inconsistency in Floating-point to Fixed-point Conversion Across Different Platforms #69

Open justinzhuguangwen opened 1 week ago

justinzhuguangwen commented 1 week ago

I've noticed that the current implementation of the floating-point to fixed-point conversion in the code might lead to inconsistent results across different platforms. The issue stems from the direct manipulation and rounding of floating-point numbers, which can behave differently on various platforms due to differences in floating-point arithmetic implementation.

Here is the current implementation for reference:

template <typename T, typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
constexpr inline explicit fixed(T val) noexcept
    : m_value(static_cast<BaseType>((EnableRounding) ?
           (val >= 0.0) ? (val * FRACTION_MULT + T{0.5}) : (val * FRACTION_MULT - T{0.5})
          : (val * FRACTION_MULT)))
{}

Here's the suggested approach for reference: https://github.com/SkynetNext/Fixed64/blob/main/include/Fixed64.h

template <typename T>
void parseFloat(const T &value)
{
  int64_t integerPart = static_cast<int64_t>(std::floor(value));
  T fractionalPart = value - integerPart;
  this->value = (integerPart << FixLut::PRECISION) +
                static_cast<int64_t>(fractionalPart * FixLut::ONE);
}

By first using std::floor to isolate the integer part and then handling the fractional part separately, this method avoids some of the pitfalls associated with direct floating-point arithmetic and rounding.