Advertisement
herhor67

ALFPN

May 27th, 2025
656
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 26.84 KB | Source Code | 0 0
  1. #pragma once
  2.  
  3. //#include <cstdint>
  4. //#include <cstring>
  5.  
  6. #include <iostream>
  7. #include <bitset>
  8.  
  9. #include <cmath>
  10. #include <cfloat> // FLT_ROUNDS
  11.  
  12. #include <bit>
  13. #include <type_traits>
  14. #include <limits>
  15.  
  16.  
  17. // Template for arbitrary float representation
  18.  
  19. // Resources:
  20. // https://stackoverflow.com/a/3542975/7321403
  21. // https://en.wikipedia.org/wiki/Minifloat
  22.  
  23. #if (__cplusplus >= 202302L)
  24. #define cmath_var constexpr
  25. #define cmath_ret constexpr
  26. #else
  27. #define cmath_var const
  28. #define cmath_ret
  29. #endif
  30.  
  31.  
  32. //#define DEBUG_ROUNDING
  33.  
  34.  
  35. namespace ALFPN
  36. {
  37.  
  38. #ifndef ALFPN_EXT_TYPES
  39.     using exp_t = std::intmax_t;
  40.     using mnt_t = std::uintmax_t;
  41. #endif
  42.  
  43.     static_assert(std::is_signed_v<exp_t>);
  44.     static_assert(std::is_unsigned_v<mnt_t>);
  45.  
  46.     using bits_t = int; // size_t;// unsigned int;
  47.  
  48.     namespace
  49.     {
  50.         template <typename T>
  51.         constexpr T safe_shift_left(T val, bits_t shift)
  52.         {
  53.             static_assert(std::is_integral_v<T>, "T must be an integral type");
  54.  
  55.             constexpr size_t bit_width = sizeof(T) * CHAR_BIT;
  56.  
  57.             if (shift >= bit_width)
  58.                 return 0;
  59.  
  60.             return val << shift;
  61.         }
  62.  
  63.         template <typename T>
  64.         constexpr T safe_shift_right(T val, bits_t shift)
  65.         {
  66.             static_assert(std::is_integral_v<T>, "T must be an integral type");
  67.  
  68.             constexpr size_t bit_width = sizeof(T) * CHAR_BIT;
  69.  
  70.             if (shift >= bit_width)
  71.                 return (std::is_signed_v<T> && val < 0) ? -1 : 0;
  72.  
  73.             return val >> shift;
  74.         }
  75.  
  76.         template <typename T>
  77.         constexpr T safe_shift(T val, int shift)
  78.         {
  79.             static_assert(std::is_integral_v<T>, "T must be an integral type");
  80.  
  81.             size_t abssh = std::abs(shift);
  82.  
  83.             if (shift < 0) // "decrease" power
  84.                 return safe_shift_right(val, abssh);
  85.             else
  86.                 return safe_shift_left(val, abssh);
  87.         }
  88.  
  89.         template <typename T>
  90.         constexpr T cexpr_exp2(int n)
  91.         {
  92.             T result = 1;
  93.             T base = 2;
  94.  
  95.             bool neg = n < 0;
  96.             n = neg ? -n : n;
  97.  
  98.             while (n)
  99.             {
  100.                 if (n % 2)
  101.                     result *= base;
  102.  
  103.                 base *= base;
  104.                 n /= 2;
  105.             }
  106.  
  107.             return neg ? (1 / result) : result;
  108.         }
  109.  
  110.  
  111.         template <typename T>
  112.         constexpr T mask_lowest_N_bits(size_t N)
  113.         {
  114.             using UT = std::make_unsigned_t<T>;
  115.             constexpr size_t len = sizeof(T) * CHAR_BIT;
  116.  
  117.             bool nonzero = N; // (N != 0)
  118.             //bits_t subn_pwr = (len - N % len) % len;
  119.             if (N > len)
  120.                 N = len;
  121.  
  122.             size_t shift = (len - N) % len;
  123.             //size_t subn_pwr = (0 - N) & (len-1);
  124.             return static_cast<UT>(static_cast<UT>(0) - static_cast<UT>(nonzero)) >> shift;
  125.         }
  126.  
  127.         /*/
  128.         template <typename T>
  129.         constexpr T mask_highest_N_bits(size_t N)
  130.         {
  131.             using UT = std::make_unsigned_t<T>;
  132.             constexpr size_t len = sizeof(T) * CHAR_BIT;
  133.  
  134.             bool nonzero = N; // (N != 0)
  135.             //bits_t subn_pwr = (len - N % len) % len;
  136.             size_t subn_pwr = (0 - N) % len;
  137.             //size_t subn_pwr = (0 - N) & (len-1);
  138.             return static_cast<UT>(static_cast<UT>(0) - static_cast<UT>(nonzero)) << subn_pwr;
  139.         }
  140.         //*/
  141.  
  142.         template<typename T>
  143.         constexpr bool check_sub(T a, T b)
  144.         {
  145.             static_assert(std::is_integral_v<T>, "Only supports integers");
  146.  
  147.             if (b < 0)
  148.                 return a <= std::numeric_limits<T>::max() + b;
  149.             else
  150.                 return a >= std::numeric_limits<T>::min() + b;
  151.         }
  152.  
  153.         constexpr bits_t width1b = sizeof(uint_least8_t) * CHAR_BIT;
  154.         constexpr bits_t width2b = sizeof(uint_least16_t) * CHAR_BIT;
  155.         constexpr bits_t width4b = sizeof(uint_least32_t) * CHAR_BIT;
  156.         constexpr bits_t width8b = sizeof(uint_least64_t) * CHAR_BIT;
  157.  
  158.         template <size_t N>
  159.         using LeastUIntBits = std::tuple_element_t< (N > width1b) + (N > width2b) + (N > width4b) + (N > width8b),
  160.             std::tuple<uint_least8_t, uint_least16_t, uint_least32_t, uint_least64_t, uintmax_t>
  161.         >;
  162.  
  163.         class NumberParent {};
  164.  
  165.         template <class T>
  166.         constexpr bool is_instance_of_Number_v = std::is_base_of_v<NumberParent, T>;
  167.  
  168.     }
  169.  
  170.     //========================================//
  171.     //========================================//
  172.     //========================================//
  173.  
  174.     constexpr bits_t ExpMaxBits = sizeof(exp_t) * CHAR_BIT;
  175.     constexpr bits_t MntMaxBits = sizeof(mnt_t) * CHAR_BIT;
  176.  
  177.     constexpr exp_t ExpBjs_Auto = std::numeric_limits<exp_t>::min();
  178.  
  179.     enum class FPclss
  180.     {
  181.         Zero = FP_ZERO,
  182.         Normal = FP_NORMAL,
  183.         Subnorm = FP_SUBNORMAL,
  184.         Inf = FP_INFINITE,
  185.         NaN = FP_NAN,
  186.     };
  187.  
  188.     using FPrndg = std::float_round_style;
  189.  
  190.     //========================================//
  191.     //========================================//
  192.  
  193.     template<
  194.         size_t P_ExpBits,
  195.         size_t P_MntBits,
  196.         bool P_IsSigned = true,
  197.         bool P_HasInfNan = true,
  198.         exp_t P_ExpBjs = ExpBjs_Auto
  199.     >
  200.     class Number;
  201.  
  202.     //========================================//
  203.     //========================================//
  204.  
  205.     template<typename T, typename = void>
  206.     struct native_type_info {};
  207.  
  208.  
  209.     //template<std::floating_point F>
  210.     template<typename F>
  211.     struct native_type_info<F, std::enable_if_t<std::is_floating_point_v<F>>>
  212.     {
  213.     public:
  214.         static constexpr bool has_sgn = std::numeric_limits<F>::is_signed;
  215.         static constexpr bool has_infnan = std::numeric_limits<F>::has_infinity || std::numeric_limits<F>::has_quiet_NaN || std::numeric_limits<F>::has_signaling_NaN;
  216.  
  217.     private:
  218.         static constexpr size_t mexp = std::numeric_limits<F>::max_exponent - std::numeric_limits<F>::min_exponent + has_infnan;
  219.  
  220.     public:
  221.         static constexpr std::size_t expb = std::bit_width(mexp);               // count bits of exponent
  222.         static constexpr std::size_t mntb = std::numeric_limits<F>::digits - 1; // subtract one implicit bit
  223.  
  224.         static constexpr exp_t expbjs = ExpBjs_Auto;
  225.     };
  226.  
  227.     //template<std::integral I>
  228.     template<typename I>
  229.     struct native_type_info<I, std::enable_if_t<std::is_integral_v<I>>>
  230.     {
  231.     public:
  232.         static constexpr bool has_sgn = std::numeric_limits<I>::is_signed;
  233.         static constexpr bool has_infnan = false;
  234.  
  235.         static constexpr std::size_t expb = 0;                              // 0 of exponent
  236.         static constexpr std::size_t mntb = std::numeric_limits<I>::digits; // sign already subtracted
  237.  
  238.         //static constexpr exp_t expbjs = -(static_cast<exp_t>(1) << (mntb - 1));
  239.         static constexpr exp_t expbjs = -static_cast<exp_t>(mntb) + 1;
  240.     };
  241.  
  242.     template<typename T, typename = void>
  243.     struct from_native
  244.     {
  245.     private:
  246.         using TI = native_type_info<T>;
  247.     public:
  248.         using type = Number<TI::expb, TI::mntb, TI::has_sgn, TI::has_infnan, TI::expbjs>;
  249.     };
  250.  
  251.     template<typename T>
  252.     using from_native_t = from_native<T>::type;
  253.  
  254.     //========================================//
  255.  
  256.  
  257.     // Struct for storing "unpacked" float data - widest possible exponent and widest possible mantissa.
  258.     struct Unpacked
  259.     {
  260.         template <typename F, std::enable_if_t<std::is_floating_point_v<F>, bool> = true>
  261.         //static constexpr bits_t TypeMntBits = std::min<bits_t>(MntMaxBits, std::numeric_limits<F>::digits - 1); // -0 works, -1 works, -2 and more fails
  262.         static constexpr bits_t TypeMntBits = std::numeric_limits<F>::digits - 1; // -0 works, -1 works, -2 and more fails
  263.  
  264.         template <typename F, std::enable_if_t<std::is_floating_point_v<F>, bool> = true>
  265.         static constexpr F TypeMntMult = cexpr_exp2<F>(TypeMntBits<F>); // std::ldexp(static_cast<F>(1), TypeMntBits<F>);
  266.  
  267.     public:
  268.         FPclss clss = FPclss::Zero; // Class cannot be Subnorm (except as return from .format). Exp and Mnt are meaningless if class is not (Sub)Normal.
  269.         exp_t e = 0;    // ALWAYS unbiased   (actual power of 2)
  270.         mnt_t m = 0;    // ALWAYS normalized (implicit 1.mmmmmm) (except when Subnorm as return from .format).
  271.         bool s = false;
  272.  
  273.         Unpacked() = default;
  274.         ~Unpacked() = default;
  275.  
  276.         Unpacked(const Unpacked&) = default;
  277.         Unpacked(Unpacked&&) = default;
  278.  
  279.         Unpacked& operator=(const Unpacked&) = default;
  280.  
  281.         template <typename F, std::enable_if_t<std::is_floating_point_v<F>, bool> = true>
  282.         cmath_ret Unpacked(F f)
  283.         {
  284.             clss = static_cast<FPclss>(std::fpclassify(f));
  285.  
  286.             s = std::signbit(f);
  287.             f = std::abs(f);
  288.  
  289.             switch (clss)
  290.             {
  291.             case FPclss::Subnorm:
  292.             case FPclss::Normal:
  293.             {
  294.                 clss = FPclss::Normal;
  295.  
  296.                 e = std::ilogb(f); // get exponent
  297.  
  298.                 f /= std::scalbn(static_cast<F>(1), e); // normalize to [1, 2)
  299.  
  300.                 // choose (A) or (B) or (C)
  301.                 // I dont think working in subnormal range (if it even exists) is a good idea, so I'd skip (A)
  302.                 // (B) seems faster than (C) but idk
  303.  
  304.                 //f -= 1;           // (A) subtract implicit bit, [1, 2) => [0, 1)
  305.                 //f *= TypeMntMult<F>;  // (A) convert to "integer"
  306.  
  307.                 f *= TypeMntMult<F>;    // (B) convert to "integer"
  308.                 f -= TypeMntMult<F>;    // (B) subtract implicit bit, "[1, 2)" => "[0, 1)"
  309.  
  310.                 //f = std::fma(f, TypeMntMult<F>, -TypeMntMult<F>); // (C) fused MultiplyAdd, same as (B)
  311.  
  312.                 f = std::nearbyint(f); // round before casting, in case of precision greater than MntMaxBits
  313.  
  314.                 m = static_cast<mnt_t>(f) << (MntMaxBits - TypeMntBits<F>);
  315.  
  316.                 break;
  317.             }
  318.             case FPclss::Zero:
  319.             case FPclss::Inf:
  320.             case FPclss::NaN:
  321.                 break;
  322.             default:
  323.                 throw std::invalid_argument("Invalid classification");
  324.             }
  325.         }
  326.  
  327.         template <typename I, std::enable_if_t<std::is_integral_v<I>, bool> = true>
  328.         cmath_ret Unpacked(I i)
  329.         {
  330.             using UI = std::make_unsigned_t<I>;
  331.  
  332.             if (i == 0)
  333.             {
  334.                 clss = FPclss::Zero;
  335.                 return;
  336.             }
  337.  
  338.             clss = FPclss::Normal;
  339.  
  340.             UI ui = i;
  341.  
  342.             if constexpr (std::is_signed_v<I>)
  343.             {
  344.                 s = (i < 0);
  345.  
  346.                 if (s)
  347.                     ui = static_cast<UI>(0) - ui; // abs with type change
  348.             }
  349.            
  350.             m = static_cast<mnt_t>(ui);
  351.  
  352.             size_t pos = std::countl_zero(m);
  353.  
  354.             e = MntMaxBits - (pos + 1);
  355.  
  356.             //m = safe_shift_left(m, pos+1);
  357.             m <<= pos;
  358.             m <<= 1;    // remove implicit 1.
  359.  
  360.         }
  361.  
  362.         template <typename F, std::enable_if_t<std::is_floating_point_v<F>, bool> = true>
  363.         explicit cmath_ret operator F () const // reverse the code of constructor
  364.         {
  365.             F f;
  366.  
  367.             Unpacked n = format<false>(std::numeric_limits<F>::max_exponent - 1, std::numeric_limits<F>::min_exponent - 1, std::numeric_limits<F>::digits - 1);
  368.  
  369.             switch (n.clss)
  370.             {
  371.             case FPclss::Normal:
  372.             {
  373.                 f = static_cast<F>(n.m >> (MntMaxBits - TypeMntBits<F>));
  374.  
  375.                 // choose (A) or (B)
  376.                 // I dont think working in subnormal range (if it even exists) is a good idea, so I'd skip (A)
  377.  
  378.                 //f /= TypeMntMult<F>;  // (A) deconvert from "integer"
  379.                 //f += 1;           // (A) add implicit bit, normalize [0, 1) => [1, 2)
  380.  
  381.                 f += TypeMntMult<F>;    // (B) add implicit bit, normalize "[0, 1)" => "[1, 2)"
  382.                 f /= TypeMntMult<F>;    // (B) deconvert from "integer"
  383.  
  384.                 f *= std::scalbn(static_cast<F>(1), n.e);
  385.  
  386.                 break;
  387.             }
  388.             case FPclss::Zero:
  389.                 f = static_cast<F>(0);
  390.                 break;
  391.             case FPclss::Inf:
  392.                 f = std::numeric_limits<F>::infinity();
  393.                 break;
  394.             case FPclss::NaN:
  395.                 f = std::numeric_limits<F>::quiet_NaN();
  396.                 break;
  397.  
  398.             default:
  399.                 throw std::invalid_argument("Invalid classification");
  400.             }
  401.  
  402.             //if (u.s)
  403.             //  f = std::copysign(f, static_cast<F>(-1));
  404.                 //f = -f;
  405.  
  406.             f = std::copysign(f, static_cast<F>(0) - n.s);
  407.  
  408.             return f;
  409.         }
  410.        
  411.         template <typename I, std::enable_if_t<std::is_integral_v<I>, bool> = true>
  412.         explicit cmath_ret operator I () const
  413.         {
  414.             I i;
  415.  
  416.             Unpacked n = format<true>(-std::numeric_limits<I>::digits, -std::numeric_limits<I>::digits + 1, std::numeric_limits<I>::digits);
  417.  
  418.             std::cout << n << std::endl;
  419.  
  420.             switch (n.clss)
  421.             {
  422.             case FPclss::Subnorm:
  423.             {
  424.                 i = static_cast<I>(n.m >> (MntMaxBits - std::numeric_limits<I>::digits));
  425.                 break;
  426.             }
  427.             case FPclss::Zero:
  428.                 i = static_cast<I>(0);
  429.                 break;
  430.             case FPclss::Inf:
  431.                 i = s ? std::numeric_limits<I>::min() : std::numeric_limits<I>::max();
  432.                 break;
  433.             case FPclss::NaN:
  434.                 i = static_cast<I>(0);
  435.                 break;
  436.  
  437.             default:
  438.                 throw std::invalid_argument("Invalid classification");
  439.             }
  440.  
  441.             if (n.s)
  442.                 i = -i;
  443.  
  444.             return i;
  445.         }
  446.  
  447.  
  448.         template <bool subnormalize = false>
  449.         Unpacked format(exp_t ExpMaxVal, exp_t ExpMinVal, size_t MntBits) const
  450.         {
  451.             FPrndg rndg = static_cast<FPrndg>(FLT_ROUNDS);
  452.  
  453.             const exp_t ExpSubVal = ExpMinVal - static_cast<exp_t>(MntBits);
  454.  
  455.             Unpacked ret(*this);
  456.  
  457.             switch (clss)
  458.             {
  459.             case FPclss::Normal:
  460.             {
  461.                 exp_t subn_pwr = (e < ExpMinVal) ? (ExpMinVal - e) : 0;
  462.  
  463.                 const mnt_t RMNDR_MASK = mask_lowest_N_bits<mnt_t>(MntMaxBits - MntBits + subn_pwr);
  464.                 const mnt_t STICKY_MASK = mask_lowest_N_bits<mnt_t>(MntMaxBits - MntBits + subn_pwr - 1);
  465.                 const mnt_t RNDGBIT_MASK = RMNDR_MASK & ~STICKY_MASK;
  466.  
  467.                 const mnt_t UNCHNGD_MASK = ~RMNDR_MASK;
  468.                 const mnt_t RSLTLSB_MASK = UNCHNGD_MASK & ~(UNCHNGD_MASK << 1);
  469.  
  470.                 bool roundup = false;
  471.                 bool denyinf = false;
  472.                 bool denyzro = false;
  473.  
  474.                 switch (rndg)
  475.                 {
  476.                 case FPrndg::round_toward_zero: // simple clear
  477.                     roundup = false;
  478.                     denyinf = true;
  479.                     denyzro = false;
  480.                     break;
  481.  
  482.                 case FPrndg::round_toward_infinity: // round up if positive
  483.                     roundup = !s && (m & RMNDR_MASK);
  484.                     denyinf = s;
  485.                     denyzro = !s;
  486.                     break;
  487.  
  488.                 case FPrndg::round_toward_neg_infinity: // round up if negative
  489.                     roundup = s && (m & RMNDR_MASK);
  490.                     denyinf = !s;
  491.                     denyzro = s;
  492.                     break;
  493.  
  494.                 case FPrndg::round_to_nearest: // oh god
  495.                 case FPrndg::round_indeterminate:
  496.                 {
  497.                     bool rndg_bit = (m & RNDGBIT_MASK) || (!RNDGBIT_MASK);
  498.                     bool stck_bit = (m & STICKY_MASK);
  499.                     bool rslt_lsb = (m & RSLTLSB_MASK) || (!RSLTLSB_MASK && RNDGBIT_MASK);
  500.  
  501.                     roundup = rndg_bit && stck_bit || rndg_bit && rslt_lsb;
  502.                     denyinf = false;
  503.                     denyzro = false;
  504.                     break;
  505.                 }
  506.                 default:
  507.                     throw std::invalid_argument("Unknown rounding mode");
  508.                 }
  509.  
  510. #ifdef DEBUG_ROUNDING
  511.                 std::cout << "\n\n";
  512.                 PRTB((((RMNDR_MASK))));
  513.                 PRTB((((UNCHNGD_MASK))));
  514.                 PRTB((((STICKY_MASK))));
  515.                 PRTB((((RNDGBIT_MASK))));
  516.                 PRTB((((RSLTLSB_MASK))));
  517.                 std::cout << "\n";
  518.                 PRTB(((m | m | m | m)));
  519.                 PRTB((m & RNDGBIT_MASK));
  520.                 PRTB((m & STICKY_MASK));
  521.                 PRTB((m & RSLTLSB_MASK));
  522. #endif
  523.  
  524.                 ret.m &= UNCHNGD_MASK;
  525.  
  526.                 if (roundup)
  527.                 {
  528.                     ret.m += RSLTLSB_MASK;
  529.  
  530.                     if (ret.m == 0)// [[unlikely]]
  531.                     {
  532.  
  533. #ifdef DEBUG_ROUNDING
  534.                         std::cout << "Overflow of mantissa" << std::endl;
  535. #endif
  536. #ifdef DEBUG_ROUNDING
  537.                         PRTB(RSLTLSB_MASK);
  538.  
  539. #endif
  540.                         ++ret.e;
  541.  
  542.                         if (ret.e == std::numeric_limits<exp_t>::min()) [[unlikely]] // will this ever happen?
  543.                         {
  544.                             --ret.e;
  545.                             ret.clss = FPclss::Inf;
  546.                             throw std::range_error("Underflow of exponent");
  547.                             break;
  548.                         }
  549.  
  550.                     }
  551.                 }
  552.  
  553.                 if (ret.e > ExpMaxVal)
  554.                 {
  555.                     if (!denyinf)
  556.                     {
  557.                         ret.clss = FPclss::Inf;
  558.                         //ret.e = 0;
  559.                         //ret.m = 0;
  560.                         break;
  561.                     }
  562.                     else
  563.                     {
  564.                         ret.e = ExpMaxVal;
  565.                         ret.m = std::numeric_limits<mnt_t>::max();
  566.                     }
  567.                 }
  568.                 //*/
  569.                 if (ret.e < ExpSubVal)
  570.                 {
  571.                     if (!denyzro)
  572.                     {
  573.                         ret.clss = FPclss::Zero;
  574.                         //ret.e = 0;
  575.                         //ret.m = 0;
  576.                         break;
  577.                     }
  578.                     else
  579.                     {
  580.                         ret.e = ExpSubVal;
  581.                         //ret.m = 0;
  582.                     }
  583.                    
  584.                 }
  585.                 //*/
  586.  
  587.                 if constexpr (subnormalize)
  588.                     if (ret.e < ExpMinVal)
  589.                     {
  590.                         exp_t subn_pwr = ExpMinVal - ret.e;
  591.  
  592.                         ret.clss = FPclss::Subnorm;
  593.  
  594.                         ret.e += subn_pwr;
  595.  
  596.                         mnt_t l = safe_shift<mnt_t>(1, MntMaxBits - subn_pwr);
  597.  
  598.     #ifdef DEBUG_ROUNDING
  599.                         std::cout << "M, M>>, L, M|L\n";
  600.                         PRTB((ret.m | ret.m));
  601.     #endif
  602.                         ret.m >>= subn_pwr; // ok
  603.     #ifdef DEBUG_ROUNDING
  604.                         PRTB((ret.m | ret.m));
  605.                         PRTB((l | l | l | l));
  606.     #endif
  607.                         ret.m |= l;
  608.     #ifdef DEBUG_ROUNDING
  609.                         PRTB((ret.m | ret.m));
  610.                         std::cout << "\n";
  611.     #endif
  612.                     }
  613.  
  614.                 break;
  615.  
  616.             }
  617.             case FPclss::Zero:
  618.             case FPclss::Inf:
  619.             case FPclss::NaN:
  620.                 break;
  621.  
  622.             default:
  623.                 throw std::invalid_argument("Invalid classification");
  624.             }
  625.  
  626.             //ret.clss = FPclss::NaN;
  627.  
  628.             return ret;
  629.  
  630.         }
  631.  
  632.  
  633.     };
  634.  
  635.     std::ostream& operator<<(std::ostream& os, const Unpacked& u)
  636.     {
  637.         //os << "(" << std::underlying_type_t<FPclss>(u.clss) << ") " << (u.s ? '-' : '+') << "0x1." << std::bitset<MntMaxBits>(u.m) << 'p' << u.e;
  638.  
  639.         os << (u.s ? '-' : '+');
  640.  
  641.         switch (u.clss)
  642.         {
  643.         case FPclss::Normal:
  644.             os << "0x1." << std::bitset<MntMaxBits>(u.m) << 'p' << u.e; // this will fail for non-native mnt_t type
  645.             break;
  646.         case FPclss::Subnorm:
  647.             os << "0x0." << std::bitset<MntMaxBits>(u.m) << 'p' << u.e;
  648.             break;
  649.         case FPclss::Zero:
  650.             os << "0x0.0";
  651.             break;
  652.         case FPclss::Inf:
  653.             os << "Inf";
  654.             break;
  655.         case FPclss::NaN:
  656.             os << "NaN";
  657.             break;
  658.         default:
  659.             throw std::invalid_argument("Invalid classification");
  660.         }
  661.  
  662.         return os;
  663.     }
  664.  
  665.  
  666.     //========================================//
  667.     //========================================//
  668.     //========================================//
  669.  
  670. #define DOASSERT
  671.  
  672. #define DOTHROWINF
  673.  
  674.     //========================================//
  675.  
  676. #define HIDE_IMPL public
  677.  
  678. #ifdef DOASSERT
  679.     #define ALFPN_ASSERT(x) static_assert((x))
  680. #else
  681.     #define ALFPN_ASSERT(x)
  682. #endif
  683.  
  684.  
  685.     template<
  686.         size_t P_ExpBits,
  687.         size_t P_MntBits,
  688.         bool P_IsSigned,
  689.         bool P_HasInfNan,
  690.         exp_t P_ExpBjs
  691.     >
  692.     class Number : private NumberParent
  693.     {
  694.         using this_t = Number;
  695.  
  696.         //template<size_t, size_t, bool, bool, bool, exp_t>
  697.         //friend class Number;
  698.  
  699.         //
  700.  
  701.         //static_assert(P_ExpBits >= size_t(P_HasInfNan || P_HasSbnrmls));
  702.         //static_assert(P_ExpBits >= 1);
  703.         //static_assert(P_MntBits > 0);
  704.  
  705.     HIDE_IMPL:
  706.  
  707.         static constexpr size_t ExpBits = P_ExpBits;
  708.         static constexpr size_t MntBits = P_MntBits;
  709.  
  710.         static constexpr bool HAS_SGN = P_IsSigned;
  711.         static constexpr bool HAS_INF = P_HasInfNan && P_ExpBits > 0;
  712.         static constexpr bool HAS_NAN = P_HasInfNan && P_ExpBits > 0 && P_MntBits > 0;
  713.         static constexpr bool HAS_SUB = true;
  714.  
  715.         // P_ can only be used above this line
  716.  
  717.         static constexpr size_t TtlBits = MntBits + ExpBits + HAS_SGN;  // Required space
  718.  
  719.         using raw_t = LeastUIntBits<TtlBits>;
  720.         using Utype = std::make_unsigned_t<raw_t>;
  721.         using Stype = std::make_signed_t<raw_t>;
  722.         using Rtype = std::conditional_t<HAS_SGN, Stype, Utype>;
  723.  
  724.         static constexpr size_t TypeBits = sizeof(raw_t) * CHAR_BIT;    // Storage space
  725.  
  726.         // We have to make sure that the format fits in the selected underlying type
  727.         ALFPN_ASSERT(TtlBits <= TypeBits);
  728.  
  729.         //========================================//
  730.         //========================================//
  731.  
  732.     HIDE_IMPL:
  733.  
  734.         static constexpr Utype ExpNulBits = 0;
  735.         static constexpr Utype ExpAllBits = mask_lowest_N_bits<Utype>(ExpBits);
  736.         static constexpr Utype ExpMinBits = ExpNulBits + 1;         // Reduce range by 1 for subnormals
  737.         static constexpr Utype ExpMaxBits = ExpAllBits - HAS_INF;   // Reduce range by 1 for Inf/NaN
  738.  
  739.         static constexpr Utype MntZroBits = 0;
  740.         static constexpr Utype MntAllBits = mask_lowest_N_bits<Utype>(MntBits);
  741.  
  742.         //
  743.  
  744.         static constexpr size_t MntShift = 0;                   // Position of Mantissa
  745.         static constexpr size_t ExpShift = MntBits;             // Position of Exponent
  746.         static constexpr size_t SgnShift = MntBits + ExpBits;   // Position of Sign
  747.  
  748.         static constexpr Utype MntMask = MntAllBits << MntShift;                    // Bitmask of Mantissa
  749.         static constexpr Utype ExpMask = ExpAllBits << ExpShift;                    // Bitmask of Exponent
  750.         static constexpr Utype SgnMask = Utype(0 - HAS_SGN) & ~(MntMask | ExpMask); // Bitmask of Sign
  751.        
  752.         //
  753.  
  754.         static constexpr exp_t ExpBjs = (P_ExpBjs == ExpBjs_Auto) ? (ExpAllBits / 2) : P_ExpBjs;
  755.  
  756.         // The smallest and largest actual (unbiased) exponent must fit into the Unpacked format.
  757.         ALFPN_ASSERT(check_sub<exp_t>(ExpMinBits, ExpBjs));
  758.         ALFPN_ASSERT(check_sub<exp_t>(ExpMaxBits, ExpBjs));
  759.  
  760.         //static constexpr exp_t ExpNulVal = static_cast<exp_t>(ExpNulBits) - ExpBjs;   // no guarantee of existence, should be unused
  761.         //static constexpr exp_t ExpInfVal = static_cast<exp_t>(ExpAllBits) - ExpBjs;   // same
  762.         static constexpr exp_t ExpMinVal = static_cast<exp_t>(ExpMinBits) - ExpBjs;
  763.         static constexpr exp_t ExpMaxVal = static_cast<exp_t>(ExpMaxBits) - ExpBjs;
  764.  
  765.         // The smallest subnormal must fit into the Unpacked format.
  766.         ALFPN_ASSERT(check_sub<exp_t>(ExpMinVal, MntBits));
  767.  
  768.         //static constexpr exp_t ExpSubVal = ExpMinVal - static_cast<exp_t>(HAS_SUB ? MntBits : 0);
  769.  
  770.  
  771.         //ALFPN_ASSERT(ExpMinVal <= ExpMaxVal); // false for integers
  772.  
  773.         static constexpr mnt_t MntZroVal = 0; // un-use?
  774.  
  775.         //========================================//
  776.  
  777.     HIDE_IMPL:
  778.         Utype strg = 0;
  779.  
  780.         //========================================//
  781.         //========================================//
  782.  
  783.     public:
  784.         static constexpr this_t Inf()
  785.         {
  786.             if constexpr (HAS_INF)
  787.             {
  788.                 this_t temp;
  789.                 temp.set_exp_raw(ExpAllBits);
  790.                 return temp;
  791.             }
  792.             else
  793.             {
  794. #ifdef DOTHROWINF
  795.                 throw std::range_error("This format has no Inf representation!");
  796. #endif
  797.                 this_t temp;
  798.                 temp.set_exp_raw(ExpMaxBits);
  799.                 temp.set_mnt_raw(MntAllBits);
  800.                 return temp;
  801.             }
  802.         }
  803.         static constexpr this_t NaN()
  804.         {
  805.             if constexpr (HAS_NAN)
  806.             {
  807.                 this_t temp;
  808.                 temp.set_exp_raw(ExpAllBits);
  809.                 temp.set_mnt_raw(MntAllBits & ~(MntAllBits >> 1));
  810.                 return temp;
  811.  
  812.             }
  813.             else
  814.                 throw std::domain_error("This format has no NaN representation!");
  815.         }
  816.         static constexpr this_t Zero()
  817.         {
  818.             this_t temp;
  819.             return temp;
  820.         }
  821.  
  822.         //========================================//
  823.         //========================================//
  824.  
  825.     public:
  826.         Number() = default;
  827.         ~Number() = default;
  828.  
  829.         Number(const Unpacked& u)
  830.         {
  831.             pack(u);
  832.         }
  833.  
  834.         template <typename N, std::enable_if_t<std::is_arithmetic_v<N>, bool> = true>
  835.         cmath_ret Number(N n) : Number(Unpacked(n))
  836.         {
  837.         }
  838.  
  839.         constexpr Number(const Number&) = default;
  840.         constexpr Number(Number&&) = default;
  841.  
  842.         template<typename RHS, std::enable_if_t<!std::is_same_v<RHS, this_t> && is_instance_of_Number_v<RHS>, bool> = true>
  843.         Number(const RHS& rhs)
  844.         {
  845.             operator=(rhs);
  846.         }
  847.  
  848.         //========================================//
  849.         //========================================//
  850.  
  851.     HIDE_IMPL:
  852.         inline constexpr Utype get_exp_raw() const
  853.         {
  854.             return (strg & ExpMask) >> ExpShift;
  855.         }
  856.         inline constexpr Utype get_mnt_raw() const
  857.         {
  858.             return (strg & MntMask) >> MntShift;
  859.         }
  860.  
  861.         inline constexpr exp_t get_exp() const
  862.         {
  863.             return static_cast<exp_t>(get_exp_raw()) - ExpBjs;
  864.         }
  865.         inline constexpr mnt_t get_mnt() const
  866.         {
  867.             return static_cast<mnt_t>(get_mnt_raw()) << (MntMaxBits - MntBits);
  868.         }
  869.  
  870.         //========================================//
  871.  
  872.         inline constexpr void set_exp_raw(Utype e)
  873.         {
  874.             strg &= ~ExpMask;
  875.             strg |= (e << ExpShift) & ExpMask;
  876.         }
  877.         inline constexpr void set_mnt_raw(Utype m)
  878.         {
  879.             strg &= ~MntMask;
  880.             strg |= m & MntMask;
  881.         }
  882.  
  883.         inline constexpr void set_exp(exp_t e)
  884.         {
  885.             if (e < ExpMinVal || e > ExpMaxVal)
  886.                 throw std::range_error("Invalid exponent");
  887.             set_exp_raw(static_cast<Utype>(e + ExpBjs));
  888.         }
  889.         inline constexpr void set_mnt(mnt_t m)
  890.         {
  891.             mnt_t mnt = m >> (MntMaxBits - MntBits);
  892.             return set_mnt_raw(static_cast<Utype>(mnt));
  893.         }
  894.  
  895.         //========================================//
  896.  
  897.         inline constexpr void set_sgn(bool neg)
  898.         {
  899.             if constexpr (HAS_SGN)
  900.             {
  901.                 if (neg)
  902.                     strg |= SgnMask;
  903.                 else
  904.                     strg &= ~SgnMask;
  905.             }
  906.             else
  907.                 if (neg)
  908.                     throw std::range_error("This format has no sign!");
  909.         }
  910.  
  911.         //========================================//
  912.  
  913.         //public?
  914.         inline constexpr this_t& be_nan()
  915.         {
  916.             return operator=(NaN());
  917.         }
  918.         inline constexpr this_t& be_inf()
  919.         {
  920.             return operator=(Inf());
  921.         }
  922.         inline constexpr this_t& be_zero()
  923.         {
  924.             return operator=(Zero());
  925.         }
  926.  
  927.         //========================================//
  928.  
  929.  
  930.         constexpr Unpacked unpack() const
  931.         {
  932.             Unpacked u;
  933.  
  934.             u.clss = classify();
  935.  
  936.             u.s = is_neg();
  937.  
  938.             // if IF commented, Unpacked e & m can have trash - dont care, didnt ask, check your classifications
  939.             if (u.clss == FPclss::Subnorm || u.clss == FPclss::Normal)
  940.             {
  941.                 u.e = get_exp();
  942.                 u.m = get_mnt();
  943.             }
  944.  
  945.             if (u.clss == FPclss::Subnorm)
  946.             {
  947.                 auto subn_pwr = std::countl_zero(u.m) + 1;
  948.  
  949.                 u.e += 1;           // because exp is actually the smallest normal one
  950.                 u.e -= subn_pwr;    // decrease exp by subn_pwr
  951.                 u.m <<= subn_pwr;   // increase mnt by subn_pwr
  952.  
  953.                 u.clss = FPclss::Normal;
  954.             }
  955.  
  956.             return u;
  957.  
  958.         }
  959.  
  960.         constexpr void pack(const Unpacked& u)//const Unpacked& u)
  961.         {
  962.             Unpacked n = u.format<true>(ExpMaxVal, ExpMinVal, MntBits);
  963.  
  964.             //PRT(n);
  965.  
  966.             switch (n.clss)
  967.             {
  968.             case FPclss::Normal:
  969.                 set_exp(n.e);
  970.                 set_mnt(n.m);
  971.                 break;
  972.  
  973.             case FPclss::Subnorm:
  974.                 set_exp_raw(ExpNulBits);
  975.                 set_mnt(n.m);
  976.                 break;
  977.  
  978.             case FPclss::Zero:
  979.                 be_zero();
  980.                 break;
  981.             case FPclss::Inf:
  982.                 be_inf();
  983.                 break;
  984.             case FPclss::NaN:
  985.                 be_nan();
  986.                 break;
  987.  
  988.             default:
  989.                 throw std::invalid_argument("Invalid classification");
  990.             }
  991.  
  992.             set_sgn(u.s);
  993.         }
  994.  
  995.         //========================================//
  996.         //========================================//
  997.  
  998.     public:
  999.  
  1000.         constexpr FPclss classify() const
  1001.         {
  1002.             Utype exp = get_exp_raw();
  1003.             Utype mnt = get_mnt_raw();
  1004.  
  1005.             size_t eidx = (HAS_INF && exp == ExpAllBits) + (exp != ExpNulBits); // 0 = nul, 1 = xxx, 2 = inf
  1006.             size_t midx = (mnt != MntZroVal);                                   // 0 = nul, 1 = xxx
  1007.  
  1008.             static constexpr FPclss LUT[3][2] =
  1009.             {   // mnt is zero                          /   mnt is nonzero
  1010.                 {FPclss::Zero,                              FPclss::Subnorm                         }// exp is zero
  1011.                 {FPclss::Normal,                            FPclss::Normal                          }// exp is nonzero
  1012.                 {HAS_INF ? FPclss::Inf : FPclss::Normal,    HAS_NAN ? FPclss::NaN : FPclss::Normal  }// exp is inf
  1013.             };
  1014.  
  1015.             return LUT[eidx][midx];
  1016.         }
  1017.  
  1018.         //
  1019.         inline bool is_inf() const
  1020.         {
  1021.             return HAS_INF && (classify() == FPclss::Inf);
  1022.         }
  1023.         inline bool is_nan() const
  1024.         {
  1025.             return HAS_NAN && (classify() == FPclss::NaN);
  1026.         }
  1027.         inline bool is_zro() const
  1028.         {
  1029.             return classify() == FPclss::Zero;
  1030.         }
  1031.         inline bool is_sub() const
  1032.         {
  1033.             return classify() == FPclss::Subnorm;
  1034.         }
  1035.         inline bool is_neg() const
  1036.         {
  1037.             return HAS_SGN && (strg & SgnMask);
  1038.         }
  1039.  
  1040.         //========================================//
  1041.         //========================================//
  1042.  
  1043.         this_t& operator=(const this_t& rhs)
  1044.         {
  1045.             strg = rhs.strg;
  1046.             return *this;
  1047.         }
  1048.  
  1049.         template<typename RHS, std::enable_if_t<!std::is_same_v<RHS, this_t> && is_instance_of_Number_v<RHS>, bool> = true>
  1050.         this_t& operator=(const RHS& rhs)
  1051.         {
  1052.             Unpacked u = rhs.unpack();
  1053.             pack(u);
  1054.  
  1055.             return *this;
  1056.         }
  1057.  
  1058.         template <typename T, std::enable_if_t<std::is_arithmetic_v<T>, bool> = true>
  1059.         explicit constexpr operator T () const
  1060.         {
  1061.             return static_cast<T>(unpack());
  1062.         }
  1063.  
  1064.        
  1065.         Rtype export_sm() const // sign-magnitude representation
  1066.         {
  1067.             return strg;
  1068.         }
  1069.  
  1070.         void import_sm(Rtype n) const // sign-magnitude representation
  1071.         {
  1072.             strg = n;
  1073.         }
  1074.  
  1075.         Rtype export_1c() const // 1s-complement representation
  1076.         {
  1077.             return export_sm() ^ ~SgnMask;
  1078.         }
  1079.  
  1080.         void import_1c(Rtype n) const // 1s-complement representation
  1081.         {
  1082.             import_sm(n ^ ~SgnMask);
  1083.         }
  1084.  
  1085.     };
  1086.  
  1087.     //
  1088.  
  1089.     using Single = from_native<float>::type;
  1090.     using Double = from_native<double>::type;
  1091.     using LDouble = from_native<long double>::type;
  1092.  
  1093.     using Half = Number<5, 10>;
  1094.  
  1095.     //using Quad = ALFPN<int128_t, 15, 112>;
  1096.     //using Octuple = ALFPN<int256_t, 19, 236>;
  1097.  
  1098. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement