64 bit integer to double
From
Bonita Montero@Bonita.Montero@gmail.com to
comp.lang.c++ on Wed Jul 22 08:37:45 2026
From Newsgroup: comp.lang.c++
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.
--- Synchronet 3.22a-Linux NewsLink 1.2