• Advertisement
Sign in to follow this  

value one resolution unit smaller than another value?

This topic is 3290 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi and sorry for the not-so-clear subject text. Any idea what function can return me a floating value that is just a "tiny little bit" smaller than the floating value that I provided to the function? if v1 is the original value, then what is f(v1) that returns v2 which will always be: 1. v2<v1 2. v2==fmod(v2,v1) 3. v2+(any value bigger than 0)>=v1

Share this post


Link to post
Share on other sites
Advertisement
You could make a function that does it.


float NextSmallestFloatValue( float value )
{
return ( value - FLT_EPSILON );
}

Share this post


Link to post
Share on other sites
Quote:
Original post by floatingwoods
Thanks for your reply!

but.. wouldn't it rather be:

return(value*(1.0f-FLX_EPSILON));

?


I don't believe so.

Lets pretend FLT_EPSILON is something bigger just to make an example easier. Lets say FLT_EPSILON is 0.1f, and we'll say 'value' is 5.0f.

With your function
5.0f * ( 1.0f - 0.1f ) = 5.0f * 0.9f = 4.5f

With my example
5.0f - 0.1f = 4.9f

As you can see 4.9 is exactly 0.1 different from 5.0, and if 0.1 was the smallest possible float then you couldn't get any closer to 5.0 than 4.9 and still be smaller.

Share this post


Link to post
Share on other sites
Lordikon,

Then try following:

float v1=1.0f;
float v2=1.0f-FLT_EPSILON;
if (v1==v2)
printf("Same!\n");

float v1=10000.0f;
float v2=10000.0f-FLT_EPSILON;
if (v1==v2)
printf("Same!\n");

Only the second paragraph will print out the message!

Share this post


Link to post
Share on other sites
My best guess is that they floats are so close together that there may be some floating point inaccuracy. Try doing (2 * FLT_EPSILON) in the function in place of FLT_EPSILON, see what happens.

Share this post


Link to post
Share on other sites
That's exactly the point: because floating-point values don't have a fixed precision, you get what you refer to as floating point inaccuracy!

But my question was: I need a value DIFFERENT from the original, but just slightly smaller. That's why I suggested:

value*(1.0f-FLT_EPSILON)

(with your method my two values would still be the same if original value is too big)

Share this post


Link to post
Share on other sites
After testing, I found it is indeed it is floating point inaccuracy. Put a breakpoint on the line where 'float v2' is made, after you step over you'll see it remains 10000.000.

The inaccuracy happens because the float must represent 5 numbers to the left of the decimal, and three numbers to the right of the decimal place are closer to .000 then they are to .999, so it is rounded.

I'll try making a better example to explain this.

Share this post


Link to post
Share on other sites
Quote:
Original post by floatingwoods
That's exactly the point: because floating-point values don't have a fixed precision, you get what you refer to as floating point inaccuracy!

But my question was: I need a value DIFFERENT from the original, but just slightly smaller. That's why I suggested:

value*(1.0f-FLT_EPSILON)

(with your method my two values would still be the same if original value is too big)


Hmmm, I see what you mean. From you're first post I didn't get that I guess. That makes sense. That is why you said it must always work. The multiplication might work to avoid floating point inaccuracy, but I'm not sure it'd give you the next smallest value or not.

Share this post


Link to post
Share on other sites
This is not really portable, but it will probably do what you want:
float previous(float x) {
int u = *reinterpret_cast<int *>(&x);
u--;
return *reinterpret_cast<float *>(&u);
}

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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?

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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-44
1.2611686178923353638e-44
1.4012984643248170709e-44
1
0.99999994039535522461
0.99999988079071044922

Share this post


Link to post
Share on other sites
Quote:
Original post by floatingwoods
But my question was: I need a value DIFFERENT from the original, but just slightly smaller.


... why?

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
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-44
1.2611686178923353638e-44
1.4012984643248170709e-44
1
0.99999994039535522461
0.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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
Quote:
Original post by iMalc
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.

Actually, there is no need to separate the exponent and the mantissa: The code I posted works just fine, except for not handling infinity and NaN correctly and perhaps some weird behaviour around 0 (it's unclear to me what you want to happen in those cases, anyway).

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement