Last active
August 29, 2015 14:09
-
-
Save christophercrouzet/49dcd547b766030ddd2d to your computer and use it in GitHub Desktop.
Uniform distribution of floating-point values in C++.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <algorithm> | |
#include <array> | |
#include <cmath> | |
#include <random> | |
#include <iomanip> | |
#include <ios> | |
#include <iostream> | |
enum class Sign { | |
Positive, | |
Negative | |
}; | |
enum class FloatingPointSubnormal { | |
Disable, | |
Enable | |
}; | |
template<typename T> | |
struct FloatingPointTraits; | |
template<> | |
struct FloatingPointTraits<float> | |
{ static constexpr int precision = 8; }; | |
template<> | |
struct FloatingPointTraits<double> | |
{ static constexpr int precision = 16; }; | |
template<> | |
struct FloatingPointTraits<long double> | |
{ static constexpr int precision = 32; }; | |
template<typename T> | |
struct FloatingPoint | |
{ | |
private: | |
using Limits = std::numeric_limits<T>; | |
using Traits = FloatingPointTraits<T>; | |
static constexpr int smallestSubnormalNumberExponent = | |
(Limits::min_exponent - Limits::digits) + 1; | |
static constexpr int smallestNormalNumberExponent = | |
Limits::min_exponent; | |
static constexpr int biggestNumberExponent = | |
Limits::max_exponent; | |
public: | |
static constexpr int precision = Traits::precision; | |
template< | |
int T_count, | |
FloatingPointSubnormal T_subnormal = FloatingPointSubnormal::Disable | |
> | |
static std::array<T, T_count> | |
uniformDistribution(Sign sign = Sign::Positive) | |
{ | |
// Uniform distribution of floating-point values. | |
// | |
// The resulting values are guaranteed to be comprised between | |
// `numeric_limits<T>::::min()` and `numeric_limits<T>::max()` when | |
// `T_subnormal` is set to `FloatingPointSubnormal::Disable`. | |
static_assert(T_count > 0, | |
"The number of values to return must be positive " | |
"and non-null."); | |
static constexpr int smallestNumberExponent = | |
T_subnormal == FloatingPointSubnormal::Enable ? | |
smallestSubnormalNumberExponent : | |
smallestNormalNumberExponent; | |
static constexpr int numberOfExponents = | |
biggestNumberExponent - smallestNumberExponent; | |
static constexpr T exponentPadding = T_count > 1 ? | |
numberOfExponents / (T_count - 1.0f) : 0.0f; | |
std::random_device randomDevice; | |
std::mt19937 generator(randomDevice()); | |
std::uniform_real_distribution<T> distribution(T(0.5), T(1.0)); | |
std::array<T, T_count> out; | |
for (int i = 0; i < T_count; ++i) { | |
int exponent = smallestNumberExponent + i * exponentPadding; | |
T significand = sign == Sign::Positive ? | |
distribution(generator) : -distribution(generator); | |
out[i] = std::ldexp(significand, exponent); | |
} | |
if (sign == Sign::Positive) { | |
std::sort(out.begin(), out.end()); | |
} | |
else { | |
std::sort(out.begin(), out.end(), [](T a, T b) { return a > b; }); | |
} | |
return out; | |
} | |
}; | |
int main(int argc, char **argv) | |
{ | |
using T = float; | |
using FloatingPoint = FloatingPoint<T>; | |
std::cout << std::showpos | |
<< std::setprecision(FloatingPoint::precision) | |
<< std::scientific; | |
auto values = FloatingPoint::uniformDistribution<32>(); | |
for (auto value : values) { | |
std::cout << value << std::endl; | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment