...one of the most highly
regarded and expertly designed C++ library projects in the
world.
— Herb Sutter and Andrei
Alexandrescu, C++
Coding Standards
is a fast templated in-place hybrid radix/comparison algorithm much like
float_sort
,
but sorts IEEE floating-point numbers (positive, zero, NaN, and negative)
into ascending order by casting them to integers. This works because
positive IEEE floating-point numbers sort like integers with the same
bits, and negative IEEE floating-point numbers sort in the reverse order
of integers with the same bits. float_sort is roughly 2X as fast as std::sort.
integer_sort
-0.0 vs. 0.0 and NaN are given definitive ordered positions by the radix-based portion of this algorithm, where comparison-based sorting does not guarantee their relative position. The included tests avoid creating NaN and -0.0 so that results match std::sort, which is not consistent in how it handles these numbers, as they compare as equal to numbers with different values.
float_sort checks the size of the data type and whether it is castable, picking an integer type to cast to, if a casting functor isn't provided by the user.
float_mem_cast casts IEEE floating-point numbers (positive, zero, NaN,
and negative) into integers. This is an essential utility for creating
a custom rightshift functor for float_sort, when one is needed. Only
IEEE floating-point numbers of the same size as the integer type being
cast to should be used in this specialized method call. Worst-case performance
is 𝑶(N * (log2(range)/s + s)), so
is asymptotically faster than pure comparison-based algorithms. s
is max_splits, which defaults to 11, so its worst-case
with default settings for 32-bit integers is 𝑶(N * ((32/11)
slow radix-based iterations + 11 fast comparison-based iterations).
float_sort
Some performance plots of runtime vs. n and log2(range) are provided below:
See floatfunctorsample.cpp for a working example of how to sort structs with a float key:
#define CAST_TYPE int #define KEY_TYPE float
struct DATA_TYPE { KEY_TYPE key; std::string data; };
Right-shift functor:
// Casting to an integer before bitshifting struct rightshift { int operator()(const DATA_TYPE &x, const unsigned offset) const { return float_mem_cast<KEY_TYPE, CAST_TYPE>(x.key) >> offset; } };
Comparison lessthan operator<
functor:
struct lessthan { bool operator()(const DATA_TYPE &x, const DATA_TYPE &y) const { return x.key < y.key; } };
Other examples: