value one resolution unit smaller than another value?

Started by
19 comments, last by alvaro 15 years, 3 months ago
Quote:Original post by alvaro
This is not really portable, but it will probably do what you want:
*** Source Snippet Removed ***


That is an interesting way to do it, however, after testing, the thread starter's method of: value * (1.0 - FLT_EPSILON) ends up giving the same result.
Advertisement
One way to do it would be to subtract 1.0 divided by (ceil(log-base-two of the number) + 23)

This can easily be done by calling frexp, subtracting a tiny fixed constant to the significand, and then calling ldexp. See <cmath> or <math.h>

It also matters for negative numbers, whether you intend to go slightly towards zero or slightly towards negative infinity. Which does "smaller" mean?
"In order to understand recursion, you must first understand recursion."
My website dedicated to sorting algorithms
Alvaro and iMalc, thank you both of you too.

I am actually going to go with the "newVal=oldVal*(1.0f-FLT_EPSILON)". I tested it with oldVal=FLT_EPSILON and oldVal=FLT_MAX and the newVal is always different (smaller), which makes me think that the so generated newVal is always the value just after oldVal (newVal=oldVal-smallestPrecisionIncrementForOldval).
Yes, actually I don't care about negative values.
Note that multiplying by (1.0f-FLT_EPSILON) will not work for very small numbers. It also seems to go down two steps instead of one even for the simplest case (1.0f).
#include <cstdio>#include <float.h>float previous(float x) {  int u = *reinterpret_cast<int *>(&x);  u--;  return *reinterpret_cast<float *>(&u);}float previous_alt(float x) {  return x*(1.0f-FLT_EPSILON);}int main() {  int i=10;  float f=*reinterpret_cast<float *>(&i);  std::printf("%.20g\n",f);  std::printf("%.20g\n",previous(f));  std::printf("%.20g\n",previous_alt(f));  std::printf("%.20g\n",1.0f);  std::printf("%.20g\n",previous(1.0f));  std::printf("%.20g\n",previous_alt(1.0f));}


1.4012984643248170709e-441.2611686178923353638e-441.4012984643248170709e-4410.999999940395355224610.99999988079071044922
Quote:Original post by floatingwoods
But my question was: I need a value DIFFERENT from the original, but just slightly smaller.


... why?
Good question... next to curiosity, I also wanted to recycle a function for a slightly different purpose. And obviously the normal thing to do is to adjust some >, >=, < or <= comparisons. That's what I ended up doing
Quote:Original post by alvaro
Note that multiplying by (1.0f-FLT_EPSILON) will not work for very small numbers. It also seems to go down two steps instead of one even for the simplest case (1.0f).
*** Source Snippet Removed ***

1.4012984643248170709e-441.2611686178923353638e-441.4012984643248170709e-4410.999999940395355224610.99999988079071044922


Very cool, good stuff to know I guess. I'm a game developer, and even though we use almost all floats to save on processing time, we rarely run into floating point precision problems because games do not need to be that exact. Nevertheless it is cool to see how rounding affects this kind of thing.

It seems very counter intuitive that (1.0f - FLT_EPSILON) doesn't actually give you the largest float value that is still less than 1.0f.
A general solution to the problem most likely requires bitwise operations with the binary representation of the floating point number, and would become a rather large function with several conditional statements and maybe also some loops.
Standard bookmark dump for floating point arithmetic: The Goldberg (html version available, too).

Quote:Original post by Zahlman
Quote:Original post by floatingwoods
But my question was: I need a value DIFFERENT from the original, but just slightly smaller.


... why?


Maybe he is writing a ray tracer, where fighting with the epsilons is very common. To overcome such problems, even fixed point ray tracers have been tried out. And I think more applications could need such.
Quote:Original post by all_names_taken
A general solution to the problem most likely requires bitwise operations with the binary representation of the floating point number, and would become a rather large function with several conditional statements and maybe also some loops.
Actually, splitting a float into its sign, exponent and significand pieces using bitwise operations is pretty easy. I've done it before. From there it's a simple decrement to the significand, and if necessary decrement the exponent. No loops required, and not even that many lines of code.
However as I've already stated, there are functions in <cmath> that do this work for you. One function separates out the significand and exponent, and one combines these back together. Using these you can do the whole thing in 3 lines of code. There's your general solution.
"In order to understand recursion, you must first understand recursion."
My website dedicated to sorting algorithms

This topic is closed to new replies.

Advertisement