This is a kind of a blog post that you probably don't need unless you've already had an encounter with its subject.
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.
GameMaker versions released in mid-2019 and later
infinity constants and
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 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() 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.
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 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.
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!