• Hello [name]! Thanks for joining the GMC. Before making any posts in the Tech Support forum, can we suggest you read the forum rules? These are simple guidelines that we ask you to follow so that you can get the best help possible for your issue.

Windows floor() function problem

JimPancakes

Member
Please take a look at this code:

Code:
if keyboard_check_pressed(vk_space)
{
    count_char += 0.1;

    if floor(count_char) = count_char
    {
        image_alpha += 0.03;
    }
}
I also made a "draw_text" of the "count_char" variable and "floor(count_char)".

The problem is that the "if floor(count_char) = count_char" function doesn't work consistently. For example when "count_char" is 1, "floor(count_char)" is 0.

But when "count_char" is 2, 3 or 4, then "floor(count_char)" works and is the same as "count_char".

But then, when "count_char" is 5, "floor(count_char)" fumbles again and stays 4.

Please help!
 

JimPancakes

Member
This is just a floating point math issue. You can test for yourself by running the following code:
Code:
// Shows a message with "0" because 0.1+0.2 is not equal to 0.3
show_message(string((0.1+0.2) == 0.3));
Here's a website that explains the problem you're having in-depth.
https://0.30000000000000004.com/
Thanks for the answer!
Is there any obvious workaround to this?
I just want to check when count_char is an integer number.
 

kroart

Member
You can store a previous value of count_char and compare it with a current value. Something like this:
if keyboard_check_pressed(vk_space)
{
prev_count_char = count_char
count_char += 0.1;

if floor(count_char) != floor(count_char)
{
image_alpha += 0.03;
}
}

But I think a better approach will be something like this. Since you increase image_alpha every 10'th iteration:
if keyboard_check_pressed(vk_space)
{
count_char++

if (count_char == 10)
{
image_alpha += 0.03;
count_char = 0
}
}
I think this code should perform faster. And easier to read.
 

Mike

nobody important
GMC Elder
Why not just use an epsilon? This will give a +/- 0.01 range when comparing around "10"

if( abs(count_char-10) < 0.01 ){

}


there is also a math_set_epsilon() which may also do what you're wanting. But use this with care.....
 

kroart

Member
Why not just use an epsilon? This will give a +/- 0.01 range when comparing around "10"

if( abs(count_char-10) < 0.01 ){

}


there is also a math_set_epsilon() which may also do what you're wanting. But use this with care.....
So checking like "if (count_char == 10)" can fail even if increasing only with "count_char++" ?
 

TsukaYuriko

šŸŒ 
Forum Staff
Moderator
Technically, any form of arithmetic operation can "fail" (read: be inaccurate), as floating points are usually not perfectly precise. The more operations you perform, the less precise things tend to get as more and more information is lost/falsified from its true intended value.

Something as simple as 4 - 4 may not equal 0 as you'd expect due to this. You just normally don't notice it because it falls under the default epsilon, gets caught by it and is still considered equivalent. Your example most likely falls under the latter category - can theoretically fail, most likely won't if you don't touch the default epsilon.

If you're curious how far off any particular result is from failing, try outputting the number via string_format with a lot of decimal digits.
 

Mike

nobody important
GMC Elder
++ will add one. But if it was 0.000001 to start with, then it's still "out" a little.

But if you AND the value with say $FFFFFFFF then it'll turn the double/real into an actual INT (internally), then you'll get exact comparisons. Basically any "binary" operation, makes the variable an int64 (though I'm not so sure about HTML5 anymore. They've changed the internals quite a bit since I last saw it)

Code:
var r = count_char&0xffffffff;
if( r==10 ){

}
 

Alice

Toolmaker of Bucuresti
Forum Staff
Moderator
For this specific problem (i.e. perform some action on Xth occurrence/every X occurrences), I'd just get rid of fractions altogether and work on integers instead.
As long as integers aren't too large, they should avoid the rounding point issues altogether - and "too large" means millions for 32-bit floats while GM uses 64-bit floats internally (this may or may not vary between platforms).

So, with this in Create:
Code:
current_count = 0;
threshold_count = 10;
And this in Step:
Code:
if keyboard_check_pressed(vk_space)
{
   current_count++;
   if (current_count == threshold_count)
   {
        current_count = 0; // use this line if you want it every 10 keypresses, rather than on 10th keypress but not 20th or 30th etc.
        image_alpha += 0.03;
   }
}
You'll get image_alpha increase either on 10th keypress or every 10 keypresses, depending on the current_count = 0 line in the Step event.

Another alternative, when you must work with fractions (e.g. because there might be some speed-up/slow-down effect at play):
This in Create:
Code:
current_amount = 0;
amount_per_step = 0.1; // might vary, but generally should be a positive number
threshold_amount = 10;
And this in Step:
Code:
current_amount += amount_per_step;
while (current_amount >= threshold_amount) {
    current_amount -= threshold_amount;
    // apply the effect
}
Because I use >= in comparison, the float precision shouldn't matter that much (they might slow-down of speed-up the effect by some usually negligible factor) - once I cross the threshold, I get the effect and am ready to get it again.
As a bonus, it handles cases when amount per step exceeds the threshold and the effect is applied multiple times within a step.
 

GMWolf

aka fel666
Am I missing something here?
This seems like a perfect job for modulo operator! Why hasnt anyone pointed that out yet?

Code:
if ( keyboard_check_pressed(vk_space))
{
  counter ++; //increase counter by one
  if (counter % 10 == 0) {
      //Do something
      image_alpha += 0.3;
   }
}
Then, if you want the fractional number back, just divide counter by 10.
 
Top