开发者_JAVA技巧
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 7 hours ago.
The community is reviewing whether to reopen this question as of 38 mins ago.
Improve this questionI'd like to verify a piece of code works on subnormal numbers, so I'd like to generate a bunch of random subnormal single-precision numbers (including zero). How can I do this?
First, recall that a
- single-precision (float) is SignBit + 8 Exponent Bits + 23 Mantissa Bits (32 bits total)
- double-precision (double) is SignBit + 11 Exponent Bits + 52 Mantissa Bits (64 bits total)
- a subnormal is a floating-point whose exponent bits are all zero
With this in hand we have the following strategy:
- Draw 32/64 bits uniformly
- Mask out the exponent bits so they are zero
- Convert the bit pattern into a floating-point number
A caveat is that the endianness of the exponent bit mask must match the endianness of the floating-point values. This is the case for most hardware, but you should test it if you want to be exceptionally rigorous or are working on something exotic.
All that said, we get this code:
// Compile with: clang++.par -O3 -march=native test2.cpp --std=c++20 -Wall -Wextra -pedantic -Werror
#include <concepts>
#include <iostream>
#include <random>
#include <type_traits>
template<std::floating_point T>
class uniform_subnormal_distribution {
private:
// float is SignBit + 8 Exponent Bits + 23 Mantissa Bits
static constexpr uint32_t subnormal_mask32 = 0x807FFFFF;
// double is SignBit + 11 Exponent Bits + 52 Mantissa Bits
static constexpr uint64_t subnormal_mask64 = 0x800FFFFFFFFFFFFF;
public:
template<class Engine>
T operator()(Engine& eng) const {
if constexpr (std::is_same_v<T, float>){
std::uniform_int_distribution<uint32_t> dist;
// Get uniformaly distributed bits
const uint32_t bits = dist(eng);
// Make the exponent all zeros
const uint32_t subnormal_bits = bits & subnormal_mask32;
// Retrieve a floating-point value from the bits
return std::bit_cast<float, uint32_t>(subnormal_bits);
} else if constexpr (std::is_same_v<T, double>){
std::uniform_int_distribution<uint64_t> dist;
const uint64_t bits = dist(eng);
const uint64_t subnormal_bits = bits & subnormal_mask32;
return std::bit_cast<double, uint64_t>(subnormal_bits);
} else {
// can't use 'false' -- expression has to depend on a template parameter
static_assert(!sizeof(T*), "Unsupported floating-point type");
}
}
};
int main(){
std::random_device rd;
std::mt19937 mt(rd());
uniform_subnormal_distribution<float> dist;
std::vector<float> res;
for (unsigned i = 0; i < 20; i++) {
const auto float_val = dist(mt);
std::cout<<float_val<<std::endl;
res.push_back(float_val);
}
return 0;
}
How do I generate random subnormal numbers? (single-precision)
As the binary precision of single-precision is commonly 23 bits, generate 24 random bits: 1 for the sign and 23 for the significant. Otherwise adjust the size.
Scale the 23-bit unsigned integer value by
r23 * std::numeric_limits<float>::min() / 0x800000u /* 2^23 */
.Apply the random sign bit.
Not that this may also generate +/- 0.0. By spec, zeroes are not sub-normals, yet I suspect for OP's purposes generating a zero is OK. If not, add a test for zero.
No endian issues.
精彩评论