What are the main reason for Erlang numbers not having infinity and nan

What are the main reason for Erlang numbers not having infinity and nan?

1 Like

Turning the question upside down: if it did have them, what would you do with them?

I’d love to learn why Erlang is like this, historical context is always very interesting and fun :slight_smile:

Modern processors support IEEE 754, so if Erlang conformed to that we’d get a slight performance improvement when working with floats. That’d be cool.

2 Likes

Erlang and Elixir uses the infinity “hack” for timers and after clause and xmerl has handling for infinity and nan otp/lib/xmerl/src/xmerl_xsd_type.erl at 28a44634fb04b95ea666abb8aac7254e2c87ae05 · erlang/otp · GitHub so there is precedent in the codebase and documentation.

I was just recently reminded about the lack of first class citizens, as I’m working my self through a language integration which does have non finite numbers. When deciding how to handle this now and in the future some historic context is essential.

The main reason is that non-finite numbers are purely an error reporting mechanism. The designers of IEEE 754 found it more useful to report errors after the fact than immediately upon failure. Quoting Kahan:

Perhaps NaNs are widely misunderstood because they are not needed for mathematical analysis, whose sequencing is entirely logical; they are needed only for computation, with temporal sequencing that can be hard to revise, harder to reverse. NaNs must conform to mathematically consistent rules that were deduced, not invented arbitrarily, in 1977 during the design of the Intel 8087 that preceded IEEE 754. What had been missing from computation but is now supplied by NaNs is an opportunity ( not obligation ) for software ( especially when searching ) to follow an unexceptional path ( no need for exotic control structures ) to a point where an exceptional event can be appraised after the event, when additional evidence may have accrued. Deferred judgments are usually better judgments but not always, alas.

Idiomatic Erlang code rejects this, computation is only supposed to continue as expected and executing past the unexpected is generally frowned upon. Hence it makes little sense to allow them in Erlang, and for what it’s worth they’re also a footgun in other languages where careless use can lead to NaN’s not being propagated, hiding bugs.

7 Likes

No. This is arguably true for NaN, but not for the infinities. Numeric infinity has clear semantics (as an extension of the real numbers). The quote you provide only discusses NaN.

Although the extended reals (real numbers together with infinities) lack most nice algebraic properties of the reals, they are well-known in math, they’re used often already in introductory real analysis classes:

That’s very interesting, thank you!

These little decisions make up a lot of the “feel” or “flavour” of Erlang in my eyes. I’d love to be able to snap my fingers and try out Erlangs from parallel universes that made subtly different decisions, it’d be a lot of fun to see what they feel like.

Another one I think about some times is Erlang with fixed size integers. I wonder what the implications might be for how we structure our code, and performance in domains effected by our choice of integer type.

TBH, I avoid floats and stay in the integer realm whenever I can, because https://0.30000000000000004.com :sweat_smile: @juhlig once told me about a rather interesting talk he attended on the topic of floats (sadly, there seems to be no recording of it :sob:), which confirmed my standing.

4 Likes

I think this is mixing things. The infinity in timers and/or receive 
 after are only a way to say “with no timeout”, and have only a resemblance to the concept of numerical infinity. That is, it could just as well have been called forever or no_timeout or sth, meaning that whatever comes afterwards will really never happen. And while that seems pointless, as I understand it it is just a convenience feature by which you can use passed-in timeouts, which may be “no timeout” meaning “wait for however long it takes” without having to check manually all over the place.

1 Like

Sure, but that’s not relevant here at all. Fact is, floating-point, or real number representations in general, are often the best tool for the job. ML workloads, all the rage nowadays, are mostly FP operations.

1 Like

Agree. Most languages I have dealt with use “Infinity” as a way to say “bigger than I can handle” (which is not the same as Infinity), and “NaN” to say “huh, no idea”.

That said, “Infinity” is not a number, rather a
 concept? (Sorry if this is not the correct term, English is not my native language). And “NaN” is not a number already by name.

The following is mainly guesswork. Erlang comes from a practical background: Telecoms. Infinity is not something that is useful in that domain, with the exception of timeouts if you want. For the same reason, Erlang has Big Integers (meaning that there is nothing in the integer domain that it can’t handle and have to tag as “Infinity”, the only limit is given by the limits of the machine it runs on) but not Big Decimals (which of lower use in that domain, and which have their own problems).

1 Like

I disagree, but let’s just leave it at that, agree to disagree :wink:

That said, how does having Infinity and NaN as numbers (which they are NOT) help here? I mean, nothing against performace improvements, but what does 1 / 0 → ‘NaN’ or whatever help anyone with?

1 Like

Section 7 of the standard says the same for infinity (but with less background, hence the link to the lecture notes). That the default exception handling of some operations produce infinity without trapping is sometimes useful as the standard defines infinity arithmetic, but as I mentioned before it’s a footgun as there is no way to distinguish unintended overflow from infinity in the mathematical sense.

1 / 0 yields positive infinity under the default IEEE 754 exception handling, which can be useful for the reasons @nsajko stated.

x + y / z or more complicated expressions is where it goes off the rails.

3 Likes

“useful” is not necessarily correct :sweat_smile: 1/0 is tl;dr undefined, but approaches negative infinity from the left, positive infinity from the right
 So, take your pick. IMO there is no correct answer that would be actually useful :woman_shrugging:

“Can’t be answered!” > “Infinity
 I guess?” :grinning_cat_with_smiling_eyes: Just my point: It’s better to say that there is no conclusive answer (something which can be acted upon) than to return a guess which may turn out to be useful but may also be just wrong (which can’t be distinguished from having no conclusive answer).

It can be both useful and correct, it all depends on what you’re doing. The problem with the default handling is that you can get infinity out of expressions where it makes no sense at all (e.g. a + b where FLOAT_MAX - a > b).

It makes sense for x / z to yield 0 when z is actual infinity. It makes no sense at all for x / ((a + b) - c) to yield 0 when c is close to a + b, but the latter expression had a (“tiny”) accidental overflow.

2 Likes

Exactly, my point. “can”. “depends”. :sweat_smile: Personally, I prefer always correct, if possible also useful to the other way round :wink:

(I guess we’re on the same side of the argument here :slightly_smiling_face:)

Not sure I agree :sweat_smile: It may be useful, though.

Thanks, thats great. Could you foresee this decision change at all in the future, @lpil did allude to some potential performance benefits on modern processors.

Also, is there a possibility of extending the number datatypes to include decimal.

The whole point of having a floating-point standard is to be able to
use the same algorithms in otherwise different environments.
As an example, I have several Smalltalk systems. One of them handles
1.0/0.0 by raising a ZeroDivide exception.
One of them handles it the way that the IEEE/IEC standard requires as
the default behaviour.
(Raising an exception is allowed by the standard, but it has to be
explicitly enabled.)
I can port floating-point code from C to Smalltalk (e.g., for
computing a special function) quite easily on the
IEEE/IEC-compatible system. The syntax of the languages is different,
but the semantics of arithmetic is the same.
Having done that, porting the code from Smalltalk to Smalltalk
requires a serious rewrite,
pushing my numerical analysis skills to their (not very wonderful) limit.
The syntax of the language is the same, but the semantics of
arithmetic is different.
(Oh, the IEEE/IEC-compliant Smalltalk tries very hard to conform to
the letter of the Smalltalk standard.)

Programming floating-point calculations in Erlang feels like
programming back in the 1970s.
(When I used computers with base 2, base 8, base 10, and base 16 floating point,
with normalised numbers, unnormalised numbers, and with both,
and with word lengths of 32, 36, and 48 bits,
oh, and with and without signed zeros.)

If you don’t greatly care about compatibility, you might care that
not providing IEEE/IEC semantics
means that every operation needs extra expense to check whether the
inputs (x / 0.0) or outputs
(1.0e200 * 1.0e200) are legal in IEEE/IEC but forbidden in Erlang.

1 Like

That’s a misreading of what Kahan wrote.
Kahan wrote that NaNs are a delayed error-reporting mechanism.
But the “non-finite numbers” includes the infinities,
which are NOT an error-reporting mechanism.
1.0/(1.0/0.0)) is 0.0, not any kind of error code.
Gambit Scheme:

(/ 1.0 (/ 1.0 0.0))

As for delayed error-reporting, there can be several good reasons for this.

  1. There are high-performance systems in which the hardware cannot give
    precise exceptions at reasonable cost. Think of SIMD vectors, as one
    example.
  2. If a compiler can spot that the value of a subexpression is irrelevant
    to the result (given what it is able to infer about the context), then
    whether the whole expression produces the expected value or raises an
    exception depends on compiler optimisation level, which is generally a
    Bad Thing.

There are more but that’s enough for now.

1 Like

Infinity is not a number?
Sorry, wrong.
Once upon a time, people refused to accept negative numbers as numbers.
There are both infinite cardinal numbers (of which $\aleph_0$ is the smallest)
and infinite ordinal numbers (of which $\omega$ is the smallest).
Infinities are perfectly good surreal numbers.
(The value of a two-player game may be a surreal number, not just a real.)
Then there are the hyperreal numbers, with infinities and infinitesimals,
showing that Newton’s understanding of calculus was actually coherent.

I am not sure that I could give you a definition of “number” that
included surreals and quaternions but excluded polynomials, and I’m
not sure I’d want to. A thing is a number if and only if it is part
of a system of elements which collectively act in sufficient analogy to
the rational numbers. Will that do?

Now the extended real line is perfectly good mathematics except for the
fact that the operations are not closed. infinity - infinity and infinity / infinity
do not have definite values. So you have to extend the extended real line with at
least one more value, | (bottom), and then the system closes.

1 Like