Yup, there are good, but also silly reasons for this. Most ISAs that have an FPU have some float-to-int conversion instruction but the rules are not the same (I think they are for anything that implements IEEE754, but C predated that and still doesn’t mandate it, though C23 lets you check at compile time if the storage representation is IEEE754 [which doesn’t necessarily imply the operations are: i386, for example, operates on 80-bit floats but can store them as 32- or 64-bit IEEE754 values]).
In theory, this is a good fit for ‘implementation defined’ but that requires the compiler to write down exactly what will happen. On most ISAs, this is possible but some older ones didn’t specify the behaviour and left it up to implementations, so the resulting compiled code would behave differently on different CPU versions.
C++ recently introduce a notion of ‘erroneous behaviour’ that fits this. Ideally, compilers diagnose on erroneous behaviour. If they don’t, it may give non-deterministic values but they are stable during a program execution and may not directly cause UB. I’m not sure if this was made EB in C++. It’s similar to uninitialised values: they are now EB in C++ and reading them will give an arbitrary value but branching on that value is guaranteed to enter either the if or else branch, whereas when they were UB it could branch anywhere (or crash or do anything else).
I checked what IEEE 754 says, and I was surprised! I wonder if any languages do what it specifies.
The 2008 and 2019 editions have the following paragraph. The 1984 edition has the same effect but not so clearly set out in one place.
When a NaN or infinite operand cannot be represented in the destination format and this cannot otherwise be indicated, the invalid operation exception shall be signaled. When a numeric operand would convert to an integer outside the range of the destination format, the invalid operation exception shall be signaled if this situation cannot otherwise be indicated.
Java and Rust specify NaN converts to 0 and overflow saturates when these situations cannot otherwise be indicated.
More entertainingly,
A language standard that permits implicit conversions or expressions involving mixed types should require that these be implemented with the inexact-signaling conversion operations below.
I tend to think that when a specification has a “should”, it needs a clear discussion of the considerations for when an implementation can disregard the “should”. Even more so when the “should” is widely ignored by existing implementations, in which case the rationale needs an honest explanation of why the spec and the implementations diverge.
Yup, there are good, but also silly reasons for this. Most ISAs that have an FPU have some float-to-int conversion instruction but the rules are not the same (I think they are for anything that implements IEEE754, but C predated that and still doesn’t mandate it, though C23 lets you check at compile time if the storage representation is IEEE754 [which doesn’t necessarily imply the operations are: i386, for example, operates on 80-bit floats but can store them as 32- or 64-bit IEEE754 values]).
In theory, this is a good fit for ‘implementation defined’ but that requires the compiler to write down exactly what will happen. On most ISAs, this is possible but some older ones didn’t specify the behaviour and left it up to implementations, so the resulting compiled code would behave differently on different CPU versions.
C++ recently introduce a notion of ‘erroneous behaviour’ that fits this. Ideally, compilers diagnose on erroneous behaviour. If they don’t, it may give non-deterministic values but they are stable during a program execution and may not directly cause UB. I’m not sure if this was made EB in C++. It’s similar to uninitialised values: they are now EB in C++ and reading them will give an arbitrary value but branching on that value is guaranteed to enter either the if or else branch, whereas when they were UB it could branch anywhere (or crash or do anything else).
I am not sure if C introduced EB.
score 1 · canonical
I checked C23, and nope, EB (erroneous behavior, I had to look this up) is considered UB (undefined behavior) per 3.5.3.
score 1 · canonical
I checked what IEEE 754 says, and I was surprised! I wonder if any languages do what it specifies.
The 2008 and 2019 editions have the following paragraph. The 1984 edition has the same effect but not so clearly set out in one place.
Java and Rust specify NaN converts to 0 and overflow saturates when these situations cannot otherwise be indicated.
More entertainingly,
I tend to think that when a specification has a “should”, it needs a clear discussion of the considerations for when an implementation can disregard the “should”. Even more so when the “should” is widely ignored by existing implementations, in which case the rationale needs an honest explanation of why the spec and the implementations diverge.
score 2 · canonical