Normally, I’d totally ignore further replies to this thread, the answer by randall77 over on github is exactly what I was looking for. Thanks to @skillian for posting it here btw. I was busy and forgot.
But this last reply has so many… interesting misconceptions about what is going on here,
that I’d like to correct some of them for the benefit of anyone who stumbles upon this topic in the future.
So first of invalid numeric value
… last time I checked IEEE Std 754-2019 -1
was a perfectly fine value.
Also the program is a syntactically valid go program (it compiles fine), so it has to have some behaviour that adheres to the language specification. The behaviour might be an undefined form of failure, but the again the spec should say so.
The function you linked to, S32_IEEE754_FloatToBits
, is literally the same as math.Float32bits
, so no need to use an external library there.
Both of those functions will return the bits of the IEEE754 binary representation (binary is important here if we want to be absolutely correct, as the standard also specifies additional representations that could be used instead).
However my attempted to do a conversion as per the section Conversions between numeric types
of the language specification.
That section states:
2. When converting a floating-point number to an integer, the fraction is discarded (truncation towards zero).
Clearly that is not what your S32_IEEE754_FloatToBits
does, unless 1109917696
is the nearest closest integer to 42.0
.
Let’s just assume by CPU processor datasheet
you meant the specification for the instruction set.
Since the target I specified has a FPU and we don’t use softfloat, the OS has actually nothing to do with it.
Now if we look into the ARM Compiler toolchain Assembler Reference Version 5.03
the obvious instruction to use for this conversion is vcvt.
This however is choice made by the designer of the go runtime (not by the CPU or the OS), the could just as well implement this part is software.
Also I did not check the disassembly to see what is actually being used, but it seems like a reasonable enough guess.
So if we check the documentation for it carefully we can verify that there is a signed and unsigned variant of that instruction.
Further it specifies that you can change the rounding behaviour of that that instruction and the default is Otherwise, the operation rounds towards zero.
.
Round towards zero
in turn is a well defined operation in the IEE754 standard.
For vcvt
this means the conversion saturates that target type.
Using the f32
to u32
variant, any floating point value less than 0 will become 0 and anything larger than MaxUint32
will become MaxUint32
.
This behavior is exploited in other languages such as Rust to provide safe saturating conversions.
However not all architectures have similar instructions, which can lead to a bit of a challenge for people working on compilers if they chose to saturation to handle “overflows” in this conversion.
Hence the go runtime opted to not handle those special cases and declare them undefined behaviour,
which is totally fine by me as long as it is explained as such in the documentation.
This also explains the different behaviour on x86 where there is no direct equivalent to vcvt
.
Also since I’m running the CPU in 32bit mode (GOARCH=arm
not GOARCH=arm64
), I’d assume some extra instructions have be generated to handle the 64bit case, which might explain the different result for the 64bit conversions.
(The irony that this basically an answer to the “why does that happen” part of my initial question is not entirely lost on me, actually I only needed to know whether the behaviour was in spec.)
I somehow have a hard time believing the RaspberryPI foundation managed to convince Broadcom to produce a custom FPU for them to make the PI better for education.
At least the person who wrote the section of the language spec had to care enough to specify the behaviour for that case. Otherwise they wouldn’t have bothered to add the part that randall77 quoted in his response over at github.