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)