• 64 bit integer to double

    From Bonita Montero@3:633/10 to All on Wed Jul 22 08:37:45 2026
    I needeed a fast conversion from a 64 bit integer to a double. But with arbitrary roundging. Yes, integers may be rounded to doubles when the
    number of bits is larger than 53.
    You could leave the rounding to the processor by setting its rounding
    mode (fesetround()). But this relies on LDMXCSR and STMXCSR on x86
    systems, which is slow. So I wrote my own code. Here it is:

    #include <cstdint>
    #include <limits>
    #include "inline.hpp"

    namespace rnd_mode
    {
    constexpr int
    Nearest = 0,
    Downward = 1,
    Upward = 2,
    Zero = 3,
    Inf = 4;
    }

    template<int Mode>
    NOINLINE double u64_to_dbl( uint64_t value ) noexcept
    {
    using namespace std;
    using namespace rnd_mode;
    static_assert( sizeof( double ) == 8 && numeric_limits<double>::is_iec559 );
    int drop = 11 - countl_zero( value );
    if( drop <= 0 ) [[likely]]
    return (double)(int64_t)value;
    uint64_t mant = value >> drop;
    if constexpr( Mode == Nearest || Mode == Upward || Mode == Inf )
    {
    constexpr uint64_t All64 = -1;
    uint64_t
    droppedMask = ~(All64 << drop),
    droppedBits = value & droppedMask;
    if constexpr( Mode == Nearest )
    {
    uint64_t half = droppedMask ^ (droppedMask >> 1);
    mant += droppedBits > half;
    uint64_t odd = mant & 1;
    mant += droppedBits == half ? odd : 0;
    }
    else
    mant += droppedBits != 0;
    constexpr uint64_t MaxMant = ~(All64 << 53);
    bool overflow = mant > MaxMant;
    drop += overflow;
    mant >>= (int)overflow;
    }
    else
    static_assert( Mode == Downward || Mode == Zero );
    double xpIncr = (double)(1ll << drop);
    return xpIncr * (double)(int64_t)mant;
    }

    template<int Mode>
    NOINLINE double i64_to_dbl( int64_t value ) noexcept
    {
    using namespace std;
    using namespace rnd_mode;
    static_assert( sizeof( double ) == 8 && numeric_limits<double>::is_iec559 );
    bool neg = value < 0;
    uint64_t uValue = neg ? 0ull - (uint64_t)value : value;
    int drop = 11 - countl_zero( uValue );
    if( drop <= 0 ) [[likely]]
    return (double)value;
    uint64_t mant = uValue >> drop;
    if constexpr( Mode != Zero )
    {
    constexpr uint64_t All64 = -1;
    uint64_t
    droppedMask = ~(All64 << drop),
    droppedBits = uValue & droppedMask;
    if constexpr( Mode == Nearest )
    {
    uint64_t half = droppedMask ^ (droppedMask >> 1);
    mant += droppedBits > half;
    uint64_t odd = mant & 1;
    mant += droppedBits == half ? odd : 0;
    }
    else if constexpr( constexpr bool Up = Mode == Upward; Up || Mode ==
    Downward )
    if constexpr( Up )
    mant += (uint64_t)!neg & (uint64_t)(bool)droppedBits;
    else
    mant += (uint64_t)neg & (uint64_t)(bool)droppedBits;
    else if constexpr( Mode == Inf )
    mant += (uint64_t)(bool)droppedBits;
    else
    static_assert( false );
    constexpr uint64_t MaxMant = ~(All64 << 53);
    bool overflow = mant > MaxMant;
    drop += overflow;
    mant >>= (int)overflow;
    }
    double
    pValue = (double)(int64_t)mant,
    xpIncr = (double)(1ll << drop);
    return xpIncr * (neg ? -pValue : pValue);
    }

    The code has a fifth rounding mode over IEEE-754 which rounds
    towards +/- infinity.


    --- PyGate Linux v1.5.18
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)