ID:2596058
 
Applies to:DM Language
Status: Open

Issue hasn't been assigned a status value.
We recently discovered that text2num() will happily convert the text entities "nan", "inf" and "-inf" to their corresponding floating point values. This leaves certain code open to fun exploits, as the normal practice to validate numeric input is to do "isnum(x) && !(x <= 0)" or whatever have you. The problem is that isnum(text2num("nan")) will return true, and if your numeric validation is done in a negating fashion, then this will happily pass nan, as every greater-than or less-than comparison against nan will return false.

The immediate workaround for this is to implement a isnan macro as follows:
"#define isnan(x) ( isnum((x)) && ((x) != (x)) )"

But this does raise the point that, despite having the floating point special values around (nan, inf, -inf), DM has no tools to actually identify these values. Which is mildly curious and leaves us with a bit of a hole in how we validate user input.

So, at minimum, it might be worth adding some form of procs to identify these special values. To be clear, I have in mind isinf, isnan, and also isfinite. It might also be swell if all of these returned false in cases where "isnum(x) != true", this way, 90% of isnum(x) validation could just be replaced with isfinite(x), which only really leaves you with sub- and supernormal numbers to contend with, but range checks shooouuuld be valid against those.
I just don't really get why isnum(NaN) is true.
In response to Zewaka
Zewaka wrote:
I just don't really get why isnum(NaN) is true.

"NaN" is a special value (or values) of the IEEE 754 floating-point type which indicates some type of computation failure, or an undefined or unrepresentable quantity. It's "a number" because it's a possible value of the numeric data types float or double (as C calls them).
You can also check for infinity, negative infinity, and indeterminate.
proc
isind(n)
return n == 1.#IND

isinf(n)
return n == 1.#INF || n == -1.#INF

isnum_strict(n)
return isnum(n) && n == n && !isinf(n) && !isind(n)
Hiead, only way to check for indeterminate, is to do as described in the original post, because 1.#IND == 1.#IND is false.
In response to StyleMistake
StyleMistake wrote:
Hiead, only way to check for indeterminate, is to do as described in the original post, because 1.#IND == 1.#IND is false.

You're confusing indeterminate with NaN.
  • 1.#IND wasn't even mentioned in the original post.
  • mob/verb/Foo()
    usr << (1.#IND == 1.#IND) // 1

    // Alternatively:
    if (1.#IND == 1.#IND) usr << "Equal!"
    else usr << "Not equal!"
    Outputs "1" and "Equal!"
1.#IND == 0 also returns true though, so you probably don't want to check for that one.
In response to Exxion
Exxion wrote:
1.#IND == 0 also returns true though, so you probably don't want to check for that one.

Oof. You're right. I guess it should be removed since 1.#IND is treated as 0 everywhere from num2text to actual calculations.
mob/verb/Huh()
usr << num2text(1.#IND) // 0
usr << 10 + 1.#IND + 3 // 13
In response to Hiead
Hiead wrote:
StyleMistake wrote:
Hiead, only way to check for indeterminate, is to do as described in the original post, because 1.#IND == 1.#IND is false.

You're confusing indeterminate with NaN.

Indeterminate is NaN, it's just that compiler statically reduces all 1.#IND to null (or zero).

You can get `1.#IND` value by evaluating `0 / 1.#INF`