简体   繁体   中英

Finding the closest floating point value less than a specific integer value in C++?

I have an input floating point value that is 0.0f <= value < 1.0f (note less than one).

When multiplying this value up to a larger range, naturally the floating point precision is decreased meaning the value can end up outside of the equivalent range.

For example if I start off with a value such as:

0.99999983534521f

Then multiply it by 100, I get:

100.000000000000f

Which is fine, but how do I then reduce the floating point representation to be the nearest floating point value that is still less than 100?

I found this little manual trick:

union test
{
    int integer;
    float floating;
};

test value;

value.floating = 1.0f;

printf("%x\n", value.integer);

Then I take that hex value and reduce it by one hex digit, then set it explicitly like so:

unsigned int almost_one = 0x3f7fffff;

float value = 1.0f;

if (value >= 1.0f)      std::memcpy(&value, &almost_one, sizeof(float));

That works well for this specific value, but is there a more general approach I can use instead?

I'm hoping there's a magic instruction I'm not aware of that I can use to achieve this!

Edit: Great set of answers here, std::nextafter looks like what I'm after. Unfortunately I can't yet use C++11 math libraries so this won't work for me. To save complicating things, I'll tag this question with C++11 and accept Mike's answer below.

I've started a new question for C++03 : Alternative to C++11's std::nextafter and std::nexttoward for C++03?

I'm hoping there's a magic instruction I'm not aware of that I can use to achieve this!

If you've got a C++11 (or C99) standard library, then std::nextafter(value, 0.0f) from <cmath> (or nextafter from <math.h> ) will give you the largest representable value smaller than value .

It gives the "next" distinct value after the first argument, in the direction of the second; so here, the next distinct value closer to zero.

Sorry for the confusion, I've missed the point at the first time. What you are looking for is of course unit in the last place (ULP) , which is closely related to machine epsilon . Here is the demo:

#include <iostream>
#include <cmath>
#include <cassert>

float compute_eps() {
  float eps = 1.0f;

  // Explicit cast to `float` is needed to
  // avoid possible extra precision pitfalls.
  while (float(1.0f + eps) != 1.0f)
    eps /= 2.0f;

  return eps;
}

float ulp(float x) {
  int exp;

  frexp(x, &exp);

  static float eps = compute_eps();

  return eps * (1 << exp);
}

main() {
  float x = 100.0f;
  float y = x - ulp(x);
  float z = nextafterf(x, 0.0f);

  assert(y == z);

  std::cout.precision(20);
  std::cout << y << std::endl;
  std::cout << z << std::endl;
}

Please, understand that this answer was intended more as educational rather than practical. I wanted to show which quantities (from theory) have to be involved in order to determine neighbouring floating-point numbers. Of course one could use std::numeric_limits<T>::epsilon() to determine machine epsilon, for example. Or go ahead and use the bullet-proof nextafterf (which is likely to be implemented much more efficiently than my demo) to directly get the neighbouring floating-point number. All in all, don't judge this answer too seriously.

NOTE: Special cases of exponent (like NaN , infinity , subnormal , and etc.) are not handled in this demo. But it's pretty straightforward how to extend this demo to support them.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM