Skip to content

Instantly share code, notes, and snippets.

@christophercrouzet
Last active August 29, 2015 14:09
Show Gist options
  • Save christophercrouzet/49dcd547b766030ddd2d to your computer and use it in GitHub Desktop.
Save christophercrouzet/49dcd547b766030ddd2d to your computer and use it in GitHub Desktop.
Uniform distribution of floating-point values in C++.
#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