LWG 117 loses the sign for negative NaN on some architectures
Jonathan Wakely

Created on 2024-05-10.00:00:00 last changed 1 month ago


Date: 2024-05-13.11:14:04

Proposed resolution:

This wording is relative to N4981.

  1. Modify [ostream.inserters.arithmetic] as indicated:

    -3- When `val` is of type `float` the formatting conversion occurs as if it performed the following code fragment:
      bool failed = use_facet<
        num_put<charT, ostreambuf_iterator<charT, traits>>
          >(getloc()).put(*this, *this, fill(),
    [Note ?: When `val` is a NaN value, the conversion to `double` can alter the sign and payload. — end note]
Date: 2024-05-13.11:14:04

LWG 117 fixed insertion of a `float` into an ostream by requiring a cast to `double`. This gives a surprising result on RISC-V when inserting a negative NaN, because RISC-V floating-point instructions do not preserve the sign or payload of NaN values. This means that std::cout << -std::numeric_limits<float>::quiet_NaN() prints `"nan"` rather than `"-nan"` as most users probably expect.

11.3 section of the RISC-V ISA manual (20191213):

Except when otherwise stated, if the result of a floating-point operation is NaN, it is the canonical NaN. The canonical NaN has a positive sign and all significand bits clear except the MSB, a.k.a. the quiet bit. For single-precision floating-point, this corresponds to the pattern 0x7fc00000.

This is allowed by IEEE 754 (2019), as per section 6.3:

When either an input or result is a NaN, this standard does not interpret the sign of a NaN.

So it is standard-conforming for static_cast<double>(val) to lose the sign (and payload) of a NaN. This might also affect Apple M1 chips, if they use the ARMv8 default-NaN mode.

The current wording does not permit an implementation to use something like std::copysign(static_cast<double>(val), std::signbit(val) ? -1.0 : +1.0) to restore the sign bit. Should this be allowed? Maybe we should say that it's unspecified whether the cast to `double` preserves the sign of a NaN? If not, should we add a note about the surprising behaviour?

Date User Action Args
2024-05-10 14:49:51adminsetmessages: + msg14134
2024-05-10 00:00:00admincreate