1. Hello Guest! It's with a heavy heart that we must announce the removal of the Legacy GMC Archive. If you wish to save anything from it, now's the time! Please see this topic for more information.
    Dismiss Notice

Windows floor() function problem

Discussion in 'GameMaker Studio 2 Community Tech Support' started by JimPancakes, Nov 16, 2019.

  1. JimPancakes

    JimPancakes Member

    Joined:
    Nov 25, 2018
    Posts:
    2
    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!
     
  2. nacho_chicken

    nacho_chicken Member

    Joined:
    Jun 21, 2016
    Posts:
    427
    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/
     
    JimPancakes likes this.
  3. JimPancakes

    JimPancakes Member

    Joined:
    Nov 25, 2018
    Posts:
    2
    Thanks for the answer!
    Is there any obvious workaround to this?
    I just want to check when count_char is an integer number.
     
  4. TsukaYuriko

    TsukaYuriko Q&A Spawn Camper Forum Staff Moderator

    Joined:
    Apr 21, 2016
    Posts:
    1,764
    Use round instead of floor. That should work around the specific issue you're encountering.
     
    JimPancakes likes this.
  5. kroart

    kroart Member

    Joined:
    Jun 16, 2018
    Posts:
    21
    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.
     
  6. Mike

    Mike nobody important GMC Elder

    Joined:
    Apr 12, 2016
    Posts:
    2,412
    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.....
     
  7. kroart

    kroart Member

    Joined:
    Jun 16, 2018
    Posts:
    21
    So checking like "if (count_char == 10)" can fail even if increasing only with "count_char++" ?
     
  8. TsukaYuriko

    TsukaYuriko Q&A Spawn Camper Forum Staff Moderator

    Joined:
    Apr 21, 2016
    Posts:
    1,764
    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.
     
  9. Mike

    Mike nobody important GMC Elder

    Joined:
    Apr 12, 2016
    Posts:
    2,412
    ++ 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 ){
    
    }
    
     
    TsukaYuriko likes this.
  10. Alice

    Alice Toolmaker of Bucuresti Forum Staff Moderator

    Joined:
    Jun 20, 2016
    Posts:
    793
    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.
     
    TsukaYuriko likes this.
  11. GMWolf

    GMWolf aka fel666

    Joined:
    Jun 21, 2016
    Posts:
    3,470
    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.
     
    Homunculus and TsukaYuriko like this.

Share This Page

  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice