What kind of bugs are you concerned about, as computation would be completely based on hardware, I guess the only thing that comes to mind for me, would be sorting, but I don’t see why that would be an issue since today we already have a similar behaviour with the infinite hack.
$ python3
Python 3.12.3 (main, Nov 6 2025, 13:44:16) [GCC 13.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import math
>>> happy_fun_times = {}
>>> nan = float('nan')
>>> happy_fun_times[nan] = 0
>>> happy_fun_times[nan] = 1
>>> happy_fun_times
{nan: 1}
>>> nan = float('nan')
>>> happy_fun_times[nan] = 0
>>> happy_fun_times
{nan: 1, nan: 0}
>>>
On the surface that example looks compelling, but using floats is unreliable regardless of infinite and nan. And is strongly discouraged to use as keys in maps, if anything Erlang should raise in those use cases, if we want to be true to that ethos.
Where should this flag be set and take hold?
At the node level? Process level? Module level? Function level? No matter what you chose, it is likely that one running in this mode can’t handle what something else running the other mode sends/returns to it. So you will have to provide for both modes everywhere. And this assumes that you have control over all parts of your system and can design all parts to handle one/the other/both modes.
Exactly!!!
No way! “Discouraging” is different from forbidding, merely a way to say “watch out, know what you’re doing, you may shoot yourself in the foot”. There are perfectly good use cases for using floats as map keys, it is totally wrong for Erlang to forbid it, like saying “I know better than you yourself what is good for you”. Map keys are just one place where using floats could go sideways, but you can use floats everywhere else, too. Say, lists:usort. Why should Erlang forbid one (map keys) and allow the other (lists:usort with lists containing floats, which we can’t check for before it is too late even)?
It’s not about the hardware doing wrong computations. It is about the computations returning semantically new things, things that behave in different ways than what the existing code at all levels above a certain point was designed for. Which means it has to be adapted, some of which may not even be under your immediate control. Which means bugs, and more bugs, oh and did I mention bugs?
I’d like to add that a lot of problems are avoided by forbidding things. In C, you can go beyond the end of an array without the compiler or runtime slapping you (valid-looking garbage might not even segfault…). You’re simply supposed to Know What You’re Doing at all times.
I find the “just be perfect” line of thinking ridiculous. It is impossible to maintain the vigilance required for writing perfect C code over time, egregious bugs slip through the net all the time because all it takes is a moment of bad luck.
In Erlang, we instead recognize that it’s impossible to hold ourselves to such a high standard. We’ve added guardrails not because we do not believe ourselves competent enough to write high-quality code, but because we are mature enough to realize that regardless of competence we cannot do so at all times.
I do agree with the general idea here (Gleam goes a lot further than Erlang does in that regard!), but I think it’s a little off to compare NaN and infinity to memory violation.
There has been a great deal of work in recent years to make programming less error prone and less dependent on the programmer not making mistakes, and many of those projects and languages have had great success. However, none that I am aware of have made removing NaN and infinity part of their strategy. Given their success, I’m not convinced that having NaN would result in the problems forecast here.
Closer to home, Gleam does not have the same exception-throwing behaviour for floats that Erlang does, and when compiling to JavaScript it is possible to get both NaN and infinity. In the language’s 6 years of use by folks other than me I’ve not had a single report or observation of bugs coming from this. As a language so close and similar to Erlang that could be a useful data-point.
cheers
That is probably… different
For one, this (Inf/NaN) is not something that happens every other day, so users can by and large get away by just ignoring it. Even if it happens, Gleam started out that way - Erlang started out different. This is not to say that one is better or worse than the other, it just depends, as always ![]()
I appreciate the discussion but I would like to encourage us all to keep this as concrete and to real world problem as possible and avoid what if and what then scenarios.
So far we’ve been doing well but getting a little close to the edge here and there.
The only thing that is clear to me is, although with good intentions in mind, the situation is still that we’re incomplete and the behaviour people are argue against inf/nan are already currently existing in todays Erlang.
You can’t rely on floats regardless of they are “finite” or special value, computed on the same machine and different languages (I learned that the hard way by implementing BLAS in Rust with conformance testing against the Fortran spec implementation), we already allow floats to be used in an unsafe manner so inclusion of inf/nan wouldn’t make it less or more error prone.
I would like to end with this topic is not straightforward to grasp and highly complex so I’m delighted to see the engagement here, and especially from academia.
FWIW, I discussed this with AI (Claude, if you care). After all, this new-fangled stuff must be good for something, right? I don’t do that often and am not a prompt-doctor, so I may have influenced/biased it a little by my overall conversation with it. Or not
However, here goes.
I first had it familiarize with floats and IEEE 754.
Then had it familiarize with Erlang and the general philosophy behind it, and how it currently handles floats.
Then I asked it to sum up and rate the role of how Erlang currently handles floats in regards to its overall philosophy and standing. Claudes verdict: perfectly aligned. It brought up various arguments on its own that also appeared in this thread.
Then I asked it to argue for inclusion of IEEE 754 Inf/NaN in Erlang. The arguments it brought to the table were weak (Claude’s own statement, not mine) and took hold in fields that are generally outside the scope of where Erlang is usually employed and shines.
Then I gave it this discussion to look at (with directions to not mind the original question (the why) and the sidetrackings), assess and rate the various arguments pro and con inclusion of Inf/NaN in Erlang. Claudes verdict (given in my words): Hell, no! The arguments for it are weak. The arguments against it are strong. Plus, it would be migration hell for many years to come.
So, there…
Not putting too fine a point on it: “… and the rest is history.” Or, even more blunt: “Famous last words.” IMO ![]()
Good point… what real world problems are there with the way Erlang currently handles floats? I fail to see any, and I don’t see any benefits in changing the current behavior. Instead, I see serious problems with such a change: Things that didn’t work suddenly start working in new ways; it would be ok if we just got a wider range of floats that just worked as any other floats before - instead, we get new things that start working in new ways; instead of detecting dubious things as early as possible, we get things that disguise as valid returns and propagate and spread and finally hit you very far away from the point of origin.
Sorry for my pessimistic attitude. But it has always served me well and never failed. And in Erlang I have found a language that serves this attitude very well, and never failed me.
Prior to OTP I used to work as an SRE in broadcast television. The bulk of my job was to write software to catch both technical (e.g. excessive compression artifacts) and content errors (e.g. subtitles not corresponding to what was spoken or wonky graphical effects) in real-time, which had me knee-deep in floats for quite a few years.
My opinion is grounded in that experience. I am not arguing that Inf/NaN is useless (they’re great if used right), I am arguing that it’s not right for Erlang.
You can rely on them just fine, it’s just that Inf/NaN have sharp edges and are more annoying to deal with than the results not coming out the same depending on how the compiler orders things.
They don’t. If you overflow or divide by zero, you get an exception instead of an Inf that might stick around forever or a NaN that doesn’t even equal itself. That makes it less likely to suddenly get weird behavior out of code that works perfectly on the happy path: you crash and let the supervision tree deal with it, instead of limping along with unexpected state.
If Erlang was designed for writing high-performance math-heavy code I would fully agree that Inf/NaN would be great to have, but it’s not.
For what it’s worth, we recently had a bug parsing floats in Erlang.
Our code received binary data from a client, where the -infinity value makes sense. We tried to parse the binary with something like the block below, causing the process to get stuck in a loop.
parse_float_attribute(<<Float:64/little-float, Rest>>) -> Float;
parse_float_attribute(NotEnoughData) ->
% wait for more data
parse_float_attribute(<<NotEnoughData/binary, NewData/binary>>).
We wrongly believed that any 64-bit binary has a float representation in erlang as it does in other languages. However, we found out that a few binaries wouldn’t match the little-float so we need to handle them manually:
parse_float_attribute(<<0:48, 240, 127, _Rest/binary>>) -> infinity;
parse_float_attribute(<<0:48, 240, 255, _Rest/binary>>) -> '-infinity';
parse_float_attribute(<<_:48, 15:4, _:4, _:1, 127:7, _Rest/binary>>) -> nan;
parse_float_attribute(<<Float:64/little-float, _Rest/binary>>) -> Float;
parse_float_attribute(NotEnoughData) ->
% wait for more data
parse_float_attribute(<<NotEnoughData/binary, NewData/binary>>).
Maybe it’s worth adding the binary patterns of infinity, -infinity and nan as an example in the documentation to make it more obvious what patterns binary are not a valid “finite floating point”.
Honestly, I wasn’t aware of the IEEE-754 encodings until I needed to investigate them for this bug.
Thanks @gonzalobf that is a great example of the foot guns there is when doing language integrations, and it begs the question why doesn’t Erlang raise in those cases, you could argue that there is inconsistency as well as in the other examples.
@jhogberg I was referring to your term example, not the case of raising when trying to compute special values.
I would like to understand why you don’t think Erlang can be used scientifically, as it makes parallelism a cake walk with the ease of concurrency, are there really more to this other than leveraging the hardware and have a big enough problem. In other languages you have to reach out for MPI.
It raises just fine. It’s in a guard context so the guard fails, but if you were to do the same in a body you would get a badmatch.
I do agree it’s very surprising though.
That’s just one of myriad ways the NaN /= NaN thing rears its ugly head, and the problem is not so much what happens if you misuse it on purpose, but what happens when you make a mistake.
Preemption, introspection, immutability, etc throw a spanner in the works. It’s great for what Erlang is intended for, but because of them the sequential performance is not going to be good enough for this to make much sense. You can certainly try, but it’s generally better to reach for CUDA/OpenCL/Vulkan and use Erlang for orchestration.
I have some background in the scientific number crunching (physics), and I can assure you that Erlang will be no one’s first choice in that domain. Erlang has great runtime, nice debugging capabilities, concurrency, hot patching and all these other unique features that aren’t needed in HPC, where economics of the software development is flipped on its head due to various factors:
- Most HPC tasks are huge batch jobs that still run on time-sharing basis.
- Academia has access to free labor.
- As a corollary of 1 and 2, running code is much more expensive than developing it. As such, portability, ease of use and fancy language paradigms are less important than raw performance. That is one of the reasons why OpenCL (which is, in theory, more universal and more portable) never took off: CUDA gave people access to some low-level APIs that let people crunch matrices on their shiny workstations slightly faster. FORTRAN compilers generate faster code than naive C++ due to lack of pointer aliasing? We’ll write FORTRAN in the current year then. You get the idea.
I’ve seen a related problem in the past. Specifically: passing data from an Electric Imp device to its agent or vice versa. Unfortunately, the protocol (which uses BSON serialization) didn’t encapsulate the device/agent-specific payloads as opaque blobs, so the Erlang code was peeking inside the payload (even though it didn’t do anything with it).
Both ends use Squirrel, which allows Inf and NaN. So we had to fork the BSON parser to return special values (e.g. {nan, Bits}) for floats unrepresentable in Erlang, allowing us to pass them through Erlang and convert them back into the correct bit batterns for the other end.