GameMaker: Working with NaN and infinities

This is a kind of a blog post that you probably don't need unless you've already had an encounter with its subject.

What are these

GameMaker uses IEEE 754 double-precision (64-bit) floating-point arithmetic for operations with non-integer values, which means that you will occasionally deal with infinities and NaN.

Infinities are pretty straightforward - you may get one by dividing a positive or a negative value by zero, or simply producing a value that's too big to hold for the type (for user-defined variables, calculating 100160 through multiplication will do).
Trying to add/subtract values from an infinity will still give you an infinity, but you can change the sign of it.

NaN is trickier - you will most often see it when dividing a zero by zero, but it is also seen in other "undefined" situations like dividing an infinity by an infinity, or multiplying an infinity by zero.

As result, and particularly when dealing with user input, you may occasionally have to deal with the two.

GMS2.2.3 and newer

GameMaker versions released in mid-2019 and later have built-in NaN and infinity constants and is_nan/is_infinity functions for checking.

Therefore you can do

var f = 1/0; // or `f = infinity`
show_debug_message(f); // "inf"
show_debug_message(is_infinity(f)); // true

var n = 0/0; // or `f = NaN`
show_debug_message(n); // "NaN"
show_debug_message(is_nan(n)); // true

Older GMS2 versions

Older GMS2 versions handled NaN and infinity largely correctly, but didn't offer helper constants/functions.

Obtaining the values is still trivial, but there's a catch - GameMaker's compiler tries to collapse operations on constant expressions, and wasn't happy with the idea of dividing by zero up to GMS2.3 or so.

Therefore you'll need an intermediate variable to store a zero in.

You could have a script to initialize one and define macros, supposed named nonfin_init:

/// nonfin_init()
gml_pragma("global", "global.__zero = 0");
#macro NaN (0 / global.__zero)
#macro infinity (1 / global.__zero)

gml_pragma would cause the variable to be initialized on game start, but you could also run it yourself if you rely on execution order.


Implementing is_nan is mostly straightforward: NaN is infamous for not being equal to itself.

/// is_nan(number)
/// @param number
return !(argument0 == argument0);

This could have been just a !=, but, for reasons most likely related to epsilon, old GMS2 versions also consider NaN to not be not equal to itself.


Checking that a value is an infinity is interesting - we have more than a few options.

I think that checking for (1/inf)==0 is pretty reliable.

/// is_infinity(number)
/// @param number
return sign(1 / argument0) == 0;

(GM's logic for sign is (f >= 0 ? (f == 0 ? 0 : 1) : -1), so sign of NaN is -1).

GMS1

GMS1 is mostly the same, but, for reasons once again likely related to epsilon, considers infinity to not be equal to itself. Therefore the checks get a little strange - we also have to check for a sign (infinities are positive or negative, NaN is neither):

/// is_nan(number)
var f = argument0;
return !(f == f) && !(f > 0) && !(f < 0);
/// is_infinity(number)
var f = argument0;
return !(f == f) && ((f > 0) || (f < 0));

"constant" definitions remain the same, but you have to add them via menu:Resources➜Define Macros.

GM8.1 and earlier

As far as I can tell, pre-2012 versions of GameMaker did not really support NaN/inf - division by zero is always throws a runtime error, and an "overflown" number prints as "ERROR" rather than any representation of infinity.

Perhaps passing numbers to a DLL would allow to properly inspect it?


That is all!

Related posts:

2 thoughts on “GameMaker: Working with NaN and infinities

  1. Thank you for the GMS1 functions. Is there a way to create infinity and NaN in GMS1? This version cannot use macro afaik. Thanks!

    • It can, but instead of a line of code it’s a little editor that you open using menu:Resources ➜ Define Macros

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.