Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use types larger than 32bit #57

Open
GMellar opened this issue Sep 9, 2023 · 9 comments
Open

Use types larger than 32bit #57

GMellar opened this issue Sep 9, 2023 · 9 comments
Labels
question Further information is requested

Comments

@GMellar
Copy link

GMellar commented Sep 9, 2023

I was wondering how to use larger base types for example Q32.16 and use all the multiplication and division routines. Is there any plan to support this?

@MikeLankamp MikeLankamp added the question Further information is requested label Sep 11, 2023
@MikeLankamp
Copy link
Owner

Hi @GMellar, at the moment fpm supports any native integer type as base and intermediate type. However, unless your compiler defines a 128-bit integer type (for fpm's intermediate type), it's not possible to define a 64-bit fixed-point type.

There is an open request (#8) to support e.g. boost::multiprecision::cpp_int which would solve this.

Does this answer your question?

@peterson79
Copy link

Does this mean at the moment a Q48.16 is not possible?

@MikeLankamp
Copy link
Owner

For most common platforms, yes, it's not possible at the moment.

@id01
Copy link

id01 commented Nov 4, 2023

If you're using gcc, you can use __int128_t

@SirNate0
Copy link

For me, I had to define

// in namespace std
template <>
struct is_signed<__int128_t>: std::true_type{};

for it to work. But I was able to use a 64bit fixed point type with that.

@AbitTheGray
Copy link
Contributor

For GCC you should be able to use already mentioned __int128_t

For MSVC, there are many comments about "no 128-bit support" but... there is std::_Signed128 from __MSVC_Int128.hpp (found thanks to https://stackoverflow.com/a/76440171 ).


You may be able to use code like

#if defined(FPM_INT128)
// Already defined
#elif defined(__SIZEOF_INT128__)
using int128_t = __int128_t;
#define FPM_INT128 ::fpm::int128_t
#elif defined(_WIN32) && defined(_MSC_VER)
using int128_t = std::_Signed128;
#define FPM_INT128 ::fpm::int128_t
#else
#warning 128-bit numbers not supported.
#endif

#ifdef FPM_INT128
static_assert(sizeof(FPM_INT128) > sizeof(std::int64_t));
static_assert(std::is_signed<FPM_INT128>::value);
using fixed_56_8  = fixed<std::int64_t, FPM_INT128, 8>;
using fixed_48_16 = fixed<std::int64_t, FPM_INT128, 16>;
using fixed_32_32 = fixed<std::int64_t, FPM_INT128, 32>;
using fixed_16_48 = fixed<std::int64_t, FPM_INT128, 48>;
using fixed_8_56  = fixed<std::int64_t, FPM_INT128, 56>;
#endif

I've put it after "Convenience typedefs" block of fixed.hpp. But I cannot test it right now as MinGW supports the GCC one and I don't have MSVC at hand.

@rockingdice
Copy link

rockingdice commented Oct 28, 2024

@AbitTheGray I'm afraid it's not working, there are two issues:

  1. the std::is_signed check for the std::_Signed128 is always false since it's not an arithmetic type.
  2. if you just comment on those checks, it still fails: [error C2679: binary "/" : no operator found which takes a right-hand operand of type 'const IntermediateType' (or there is no acceptable conversion)] on :
 // Explicit conversion to a floating-point type
 template <typename T, typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
 constexpr inline explicit operator T() const noexcept
 {
     return static_cast<T>(m_value) / FRACTION_MULT;
 }

@MikeLankamp from this error, I found the comments in the code:

// Although this value fits in the BaseType in terms of bits, if there's only one integral bit, this value
// is incorrect (flips from positive to negative), so we must extend the size to IntermediateType.
static constexpr IntermediateType FRACTION_MULT = IntermediateType(1) << FractionBits;

But I don't know the meaning of "there's only one integral bit", can I change the type 'IntermediateType' to 'BaseType' if I only use 24:8 or 48:16 fpm numbers? It'll work if change that but I don't know if it will lead to some calculation errors...

Updated: I tested to change the IntermediateType to BaseType and it seems work well.

@MikeLankamp
Copy link
Owner

But I don't know the meaning of "there's only one integral bit"

That comment is relevant e.g. when you use a 1.31 fixed point type with a base type of int32_t. Then a BaseType FRACTION_MULT would be 1 << 31 (0x80000000), which is MIN_INT. This messes up the calculations using FRACTION_MULT.

can I change the type 'IntermediateType' to 'BaseType' if I only use 24:8 or 48:16 fpm numbers?

Yes.

@rockingdice
Copy link

@MikeLankamp got it, thanks for your explanation!
For others who want to use 64bit fpm numbers (like 48:16) with the MSVC compiler, you need c++20 to use std::_Signed128 or another workaround:

#include <ranges>
using int128_t = std::ranges::range_difference_t<std::ranges::iota_view<long long, long long>>;

plus you need to comment on the static checks that lead to errors.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

7 participants