Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the best way to round large unsigned floating point numbers to integers in C++?

Tags:

c++

rounding

I need to round a 64-bit double value to the nearest uint64_t in my code with the standard "math.h" midpoint rounding behaviour. Some of these numbers have the property:

  • Larger than LLONG_MAX
  • Less than or equal to ULLONG_MAX

I noticed there is no 'unsigned' version of the standard library rounding functions. Also, I suspect some of the tricks people use to do this no longer work when an intermediate double value is too large to fit into the mantissa.

What is the best way to do this conversion?

like image 771
Chuu Avatar asked Oct 16 '22 02:10

Chuu


1 Answers

I would use std::round and then clamp to uint64 range. Also you need to decide what to do with NaN.

std::round does not convert to integer. Note that the built-in floating to integer conversion in C & C++ is truncating and undefined for out of range values.

std::round implements the standard "school" type rounding to the nearest integer with the midpoint cases being rounded outwards (e.g. round(1.5) == 2; round(-1.5) = -2).

The current rounding mode is ignored, also this is not the IEEE754 rounding to nearest. The latter would use round half to even and round the tie breaking cases up and down to reduce statistical bias.

For long long one would do something like this.

double iv = std::round(val);
if (std::isnan(iv)) {
    return 0LL;
} else if (iv > LLONG_MAX)
   return LLONG_MAX;    
} else if (iv < LLONG_MIN) {
   return LLONG_MIN;
} else {
   return (long long)iv;
}

The unsigned long long case is done correspondingly.

double iv = std::round(val);
if (std::isnan(iv)) {
    return 0ULL;
} else if (iv > ULLONG_MAX)   // ok, if you know your values are less this can be spared of course
   return ULLONG_MAX;    
} else if (iv < 0) {
   return 0ULL;
} else {
   return (unsigned long long)iv;
}
like image 181
Andreas H. Avatar answered Oct 19 '22 01:10

Andreas H.