1

I need to define a "Float" type, depending on the compilation constant defining the size of the "Int" type. That constant let's call it "IntBits" may be 32, 64, 128 etc. (could be also in between 40, 48 etc). So the Float should be accordingly defined as: __float32 __float64 __float128 etc. Essentially I need to define "Float" as __float32 when IntBits<=32 as Float64 if IntBits<=64 etc.

Is there a way to avoid to use #define 's ?

cigien
  • 57,834
  • 11
  • 73
  • 112
George Kourtis
  • 2,381
  • 3
  • 18
  • 28
  • 2
    You can use `std::conditional`. See https://stackoverflow.com/questions/44550976/how-stdconditional-works. – cigien Aug 14 '23 at 17:12
  • 3
    Is this an [XY problem?](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem) On the one hand, a 32-bit floating point has less significance than a 32-bit integer. On the other, are you trying to store a n-bit floating point value in an n-bit integer variable? – Weather Vane Aug 14 '23 at 17:13
  • `typedef ... float_with_the_same_size_as_int;` -- not that that's an answer, but something like that (the `...` more specifically) is what you're looking for? – Ulrich Eckhardt Aug 14 '23 at 17:18
  • I use 31 bit integers, and 30 bit floats, plus 30 bit objects, all in one, so the same 32 bit value may signify an integer, a 30bit float, or an array of objects, but that is not the clue to the question. – George Kourtis Aug 14 '23 at 17:18
  • Then it most likely _is_ an XY problem, indeed. – Andrej Podzimek Aug 14 '23 at 17:27
  • You can use a `std::bitset<32>` to store the bits of a 32-bit `float`. – Eljay Aug 14 '23 at 17:44

3 Answers3

3

You might use:

  • std::conditional

    template <std::size_t N>
    using Float = std::conditional_t<N <= 32, float,
                  std::conditional_t<N <= 64, double,
                                              long double>>;
    
  • or partial specialization (example uses C++20):

    template <std::size_t N>
    struct FloatSelector
    {
        using type = long double;
    }
    
    template <std::size_t N>
    requires (N <= 32)
    struct FloatSelector
    {
        using type = float;
    };
    
    template <std::size_t N>
    requires (32 < N && N <= 64)
    struct FloatSelector
    {
        using type = double;
    };
    
    template <std::size_t N>
    using Float = typename FloatSelector<N>::type;
    
Jarod42
  • 203,559
  • 14
  • 181
  • 302
1

Here’s a runnable snippet that resolves a type called Int (based on its size) to a std::float…_t of equal size, at compile time:

#include <cstdint>
#include <iostream>
#include <stdfloat>

namespace {

template <std::size_t> struct Mapping;
template <> struct Mapping<2> { using Float = std::float16_t; };
template <> struct Mapping<4> { using Float = std::float32_t; };
template <> struct Mapping<8> { using Float = std::float64_t; };
template <> struct Mapping<16> { using Float = std::float128_t; };

template <typename Type>
struct Trait { using Float = Mapping<sizeof(Type)>::Float; };

}  // namespace

int main() {
  { using Int = std::int16_t;
    using Float = Trait<Int>::Float;
    std::cout << sizeof(Float) << std::endl; }
  { using Int = std::int32_t;
    using Float = Trait<Int>::Float;
    std::cout << sizeof(Float) << std::endl; }
  { using Int = std::int64_t;
    using Float = Trait<Int>::Float;
    std::cout << sizeof(Float) << std::endl; }
  { using Int = std::int8_t[16];  // no std::int128_t available yet
    using Float = Trait<Int>::Float;
    std::cout << sizeof(Float) << std::endl; }
}

Admittedly, the need for Mapping is unpleasant, but OTOH, the number of available floating-point types is not way too high or arbitrary.

There are numerous other ways to express the same thing using templates or if constexpr constructs. Also, there are (not very portable) 80-bit and other compiler-specific floating-point type aliases. Some of those aliases can be used in case C++23 and <stdfloat> is not supported yet. (At the time of this writing, g++ has <stdfloat> available, but clang++ does not.)

Andrej Podzimek
  • 2,409
  • 9
  • 12
  • Thanks for the effort put to write the code. At the beginning I thought the question would have no answer, but now I see there are more than one, and still struggling to understand them, and struggling more to individuate the best one. – George Kourtis Aug 15 '23 at 08:42
0

Given the support of previous answers I finally finalized the code as follows:

#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cstdint>
#include <type_traits> // for std::conditional

using namespace std;
template <size_t N> using Float_4=conditional< 80<N && N<=128, _Float128,bool>::type;
template <size_t N> using Float_3=conditional< 64<N && N<= 80,__float80 ,Float_4<N>>::type;
template <size_t N> using Float_2=conditional< 32<N && N<= 64, _Float64 ,Float_3<N>>::type;
template <size_t N> using Float_1=conditional< 16<N && N<= 32, _Float32 ,Float_2<N>>::type;
template <size_t N> using Float_0=conditional<  0<N && N<= 16, _Float16 ,Float_1<N>>::type;

typedef Float_0<81> Float;

int main(int argc, char **argv)
{
    Float x=12.3456789012345678901234567890123456789e-3L;
    std::cout<<"Hello x="<<setw(50)<<setprecision(40)<<x<<std::endl;
    return 0;
}

Unfortunately the code above while should work for N=15 and N=89 etc, it does not work for all cases because << is still "under development" for types as _Float16 or _Float128 :-) .

George Kourtis
  • 2,381
  • 3
  • 18
  • 28
  • @Jarod42,@andrej-podzimek I was unable to put the template "all in one", the above seems the shortest solution obtainable, in case there are other simpler solutions please inform ! – George Kourtis Aug 15 '23 at 13:31