Вы можете сделать это, используя свойства представления с плавающей запятой, предоставленные в шаблоне numeric_limits
заголовка <numeric>
и подпрограммах в <cmath>
. Проверка битов, представляющих значения, не требуется. Вот пример кода на C ++.
#include <cmath>
#include <cstdint>
#include <numeric>
/* Return the signed distance from 0 to x, measuring distance as one unit per
number representable in FPType. x must be a finite number.
*/
template<typename FPType> intmax_t ToOrdinal(FPType x)
{
static const int
Radix = std::numeric_limits<FPType>::radix,
SignificandDigits = std::numeric_limits<FPType>::digits,
MinimumExponent = std::numeric_limits<FPType>::min_exponent;
// Number of normal representable numbers for each exponent.
static const intmax_t
NumbersPerExponent = scalbn(Radix-1, SignificandDigits-1);
static_assert(std::numeric_limits<FPType>::has_denorm == std::denorm_present,
"This code presumes floating-point type has subnormal numbers.");
if (x == 0)
return 0;
// Separate the sign.
int sign = std::signbit(x) ? -1 : +1;
x = std::fabs(x);
// Separate the significand and exponent.
int exponent = std::ilogb(x)+1;
FPType fraction = std::scalbn(x, -exponent);
if (exponent < MinimumExponent)
{
// For subnormal x, adjust to its subnormal representation.
fraction = std::scalbn(fraction, exponent - MinimumExponent);
exponent = MinimumExponent;
}
/* Start with the number of representable numbers in preceding normal
exponent ranges.
*/
intmax_t count = (exponent - MinimumExponent) * NumbersPerExponent;
/* For subnormal numbers, fraction * radix ** SignificandDigits is the
number of representable numbers from 0 to x. For normal numbers,
(fraction-1) * radix ** SignificandDigits is the number of
representable numbers from the start of x's exponent range to x, and
1 * radix ** SignificandDigits is the number of representable subnormal
numbers (which we have not added into count yet). So, in either case,
adding fraction * radix ** SignificandDigits is the desired amount to
add to count.
*/
count += (intmax_t) std::scalbn(fraction, SignificandDigits);
return sign * count;
}
/* Return the number of representable numbers from x to y, including one
endpoint.
*/
template<typename FPType> intmax_t Distance(FPType y, FPType x)
{
return ToOrdinal(y) - ToOrdinal(x);
}
#include <iomanip>
#include <iostream>
template<typename FPType> static void Try(FPType x)
{
std::cout << x << " -> " << ToOrdinal(x) << ".\n";
}
int main(void)
{
Try(0.f);
Try(0x1p-149f);
Try(0x1p-127f);
Try(0x1p-126f);
Try(0x1p-126f + 0x1p-149f);
Try(1.f);
Try(1.5f);
Try(0.l);
Try(1.l);
Try(1.5l);
// Test from 3 steps below 4 to 5 steps above 4.
float x = 4, y = x;
for (int i = 0; i < 3; ++i)
x = std::nexttowardf(x, 0);
for (int i = 0; i < 5; ++i)
y = std::nexttowardf(y, INFINITY);
std::cout << "There are " << Distance(y, x) << std::setprecision(8)
<< " representable numbers from " << x << " to " << y << ".\n";
}