<table border="1" cellspacing="0" cellpadding="8">
    <tr>
        <th>Issue</th>
        <td>
            <a href=https://github.com/llvm/llvm-project/issues/61973>61973</a>
        </td>
    </tr>

    <tr>
        <th>Summary</th>
        <td>
            Inconsistent run-time (on x86_64) and compile-time folding in NaN production
        </td>
    </tr>

    <tr>
      <th>Labels</th>
      <td>
            new issue
      </td>
    </tr>

    <tr>
      <th>Assignees</th>
      <td>
      </td>
    </tr>

    <tr>
      <th>Reporter</th>
      <td>
          wjristow
      </td>
    </tr>
</table>

<pre>
    A customer of ours reported an inconvenience they hit due to the way we handle NaNs.  They reported cases where at low optimization, expressions like:

```
  0.0f  *   std::numeric_limits<float>::infinity()
```

produced `0xffc00000` (for succinctness, say `-nan`), whereas with optimization, the result was `0x7fc00000` (say, `+nan`).  The IEEE standard (IEEE Std 754™-2008) doesn't require any specific NaN representation, so both are legal.  And hence by definition, this isn't a bug.

That said, it's a problem/annoyance for them, in that they intentionally initialize some floating-point values to a NaN by multiplying +0 times +inf.  At low optimization, this results in 0xffc00000.  But with optimization, it produces 0x7fc00000 (because of compile-time folding).  They have testing tools that do bit-wise comparisons of results, and they want those bit-wise comparisons to match across optimization levels.

I initially thought that we folded the product of +0 times +inf to `+nan` because we folded in an "intuitively sensible" way (in that we produced a NaN with the sign bit set according to the XOR of the sign bits of the factors).  And I expected that -0 times +inf (or +0 times -inf) would fold to  `-nan`.  (And I thought the hardware behaved "strangely", in that it always produced a NaN with the sign-bit set -- I tried a handful of x86_64 targets, and they all produced the same negative NaN when the multiplication was done at run-time, rather than folded at compile-time; although I admit that I cannot find an x86_64 hardware spec that asserts the multiplication _will_ do that.)  But on experimenting, I found that when we fold these products at compile-time, we always fold them to `+nan`, irrespective of the sign bits of the factors.

In short, when optimization is enabled and so the following expressions are folded at compile-time:

```
    0.0f  *   std::numeric_limits<float>::infinity()
  (-0.0f) *   std::numeric_limits<float>::infinity()
    0.0f  * (-std::numeric_limits<float>::infinity())
  (-0.0f) * (-std::numeric_limits<float>::infinity())
```

they _all_ produce `0x7fc00000` (`+nan`). but if they are computed at run-time, they _all_ produce 0xffc00000 (`-nan`).

If in this folding we produced a NaN with the sign-bit being the XOR of the sign bits of the factors (that "intuitive" way), then I was going to suggest a workaround to the customer of initializing their values using the expression `-0 * +inf`.  In which case, with or without optimization, the `-nan` result would be produced (that is, whether folded to a constant at compile-time, or produced by multiplying the factors at run-time, the result would be the `-nan`).  But since we always fold to `+nan`, that idea doesn't work.

Here is a test showing the Clang (trunk) result:
https://godbolt.org/z/Es584o3eK

In that test-case, the compile-time computed results (that is, when compile-time folding is done) are all `+nan`, and the run-time results are all `-nan`.

As an experiment, I tried the same test-case with the Microsoft compiler (Version 19.29.30146 for x64), and it produced `-nan` for all the products (folded at compile-time, or computed at run-time).  This is the case with and without optimization (`/Od` and `/O2`).

FTR, I also tried GCC, and different versions had different behavior.  So there isn't much of a model that I can derive from that.

----

In summary, we could:

1. Do nothing (leaving this "inconsistent" behavior between values computed at compile-time vs at run-time, and hence often different behavior at different optimization levels).
2. Change our folding of these sorts of cases to produce a `-nan` (mimicking the Microsoft behavior, and being consistent across optimization levels (at least for hardware that produces `-nan` for these products)).
3. Change our folding to make the sign-bit of the NaN be the XOR of the sign bits of the two factors (creating the opportunity to write code that folds at compile-time in a way that matches the hardware behavior, and hence is handled consistently across optimization levels; but doesn't mimic the Microsoft behavior).

What do people think?  I lean toward option 2, although I can see an argument for option 3 (especially if other hardware always produces a `+nan` for cases where the product is computed at run-time).  There's also an argument for option 1, given that the IEEE Standard doesn't specify the behavior (and so users can not safely write code that depends on a particular behavior).
</pre>
<img width="1px" height="1px" alt="" src="http://email.email.llvm.org/o/eJysWF1v2zrS_jXKzcCGQjuOc-GLNI3fN1jsOcA5xe7eFZQ4snhCkV4OFdX99Ysh9WXHTReLFkUS2_RwPp_nGUkifbCIu-zuU3b3-Ua2oXZ-1_3lNQXX3RROnXaPULYUXIMeXAWu9QQej84HVCAtaFs6-4ZWoy0RQo0nqHUA1SIEx6-hkyfoEGpplUH4Tf5GS4AvfHC0U0pCgq5GjyADGNeBOwbd6O8yaGcz8QT47eiRSDtLYPQrZqvHLP-c5cPPTd7_jy8B8mVeAWTiEQAoKD6-erRtg16XX41udKBs9VQZJ0O2ek4fa1tpq8MpE9tMPFw1nH4evVNtiQqyTZ5_q6oy53_ZJodMbCvngdqy1LYMFonYe5InPruw0rIp8cBvxnglQadD_S5ezpxHak2ATlK66P78IpInPhkNfhotp-TCy_PzM1CQVkmv-HR848-g4P5unT2LbLvOHsVC5DnHCsoh2UzcB_D471ZzHewJ6IilrnTJVeNqeSS0YfSRHBQu1CA9gsGDNEuAR6ugjr1QnEBhTOgYkibQ_TUSivawnCf1Sy0DkNSKz-qQiXsCCUfvCoNNJvbSWneSbJkzHGp-8wm0hcBfjJ2nbUDL10lj-JUOWhr9HYFcgxCLre1hcXTaBniTpkXiLpUxvuIETWuCPpqTtgfIxKccgm6Q-E9tKw7uem_GwFKxiB2aWmIJ8KkN1yusA_R9RDDVlktVYClbQp630jVHbXDBjkDljNL2MBb5BLV8QwhIHBYE5wylbCgHhQ6LThNGE9Jr4slx1eAneyCtSnnrpOUMOsLrXwsOGhnKGmTpHdFZKGDwDQ2dlfJlyL05sdn2UIfkV5eCwHhvH35gr95lm--cdzYMSZlMaMsAlAmhbWh10G9oTkBoSRcGMyEi8mRiO7RIN9zIyBVLHuvCnjAOcuhAGECWpfMqZTR--q_f_2Af5wdpeKOSZXCeUk24-V8YqrAMMUYZYHERVya2zs_jXWhb8Qh2rjUqxsb3zuBiyTi2TbanbDKietXx7BXIfcBTLih4aQ9oTpkQ8_HQAaTp5Ik-TMFiSMFiwXd5HY8xclet4Yi_bTdfN2sI0h_wooekMZPtaFA2CBYPkiuT7qrRxo_6OdNl6iAGOOVsxH7f2tjsbNvLUCOPurRDzWU4G4ls9QmkSUmBF5Cq0X2nvUDJgBGg0jYSVe_6mDTGtnRUEqEPdM2zr5025iuPE59ccpniQDsbq-x1w3jDI_kEL1C51vZVj6H2rcqGaex2ehcDUwEO5Rm-0FwOQKym98h-x4z-pCPPB9IC1c6Hnnbs-QRrArSyMJHRFcN6tOOMcR2PwZx7OXU_qsXHjPzrODnOw4KtcUF-hb25b2z7f7T2Ywd_gdGrSiSO3lfJTdoP31Wp8E4jFG0AXfWT6xPatyEVdT6CV-xP7NZbnlTNec9VCX00DcT1MwSO8FNgRN7_DnbZgzhvcxboob9XWYHb_SVizMH1oE7t4YDEIqRz_lX6NLep7edqd1QQvUvaD6qhpcHLaTgiZOd9uRnrE3a_WOhqXdZR5cYBjGrAx9-OweSK9JvSOqrASA_FLIND7Jr6sY5oOfAry5rSWZaA4RrkOD9ZupA-8wS_74d3Dp37m5iQQZI0i7VLaHuPaikIhXKmQrkuZ930_7wbaFaELHcYzbrB1Scjo17bBt_aV5645OEISHUIR-JXYp-J_cGpwpmwdP6Qif33TOyf6W67div82wVkJmGJFBZD6WKDzCXZODeD_HtXFHtVw3EoTHnsLc8fs-dlXnpqHfM_3jH7wiAR5p4_EvPdxE-JnBKbj8Q8hjWN4N81qztXjb3CMmX7D_SxuW8fluJhucpv15sowL9t1v2EsZ-TmFXz3uWD7OlM7VHaka4ySGrL61iUNG_cIFIZRt_5_muzNALf_nfFzvC5_rW4glf7L3-kTEnDDBjT9X9PT0OESlcVeuTFIWWEoJbzt6MK084vAf6MUBIbNrVz05Y1A4qExik0M4kCCj2zeeVd04uMmU-LxWJxyeNt00h_6lVDyUN4wby3S_jswLpQpz1ma1C-pWHRlJCSgUFTiM0hRs-hwNAh2gHk5oU4a-K3d7ggx7XPVQHtlbTwN6Z3r2wQUz3EEp5qlrHgWj9OTEJ-4mXOJyJIjw2CG6lJznsvE9tGN7p8HXBi6u_Bp8HzxDlTUj7Yc9isDGBQUojtPQrKWNNxo7sYgnMJmGi9j3Z1Ndq4cr3iOTf25Be3VfwpR4bOzXmy9CjDkAt3PDofWpYZfFXndeBmUn0Y7MU7oRoXrrhUxTNxI0S6sorMMpt6QlP_-EfNkmxOH6SZhT1rlIkRYi1_WMfzUf5nvwMf0R0Nh6Tta7baA7xw4SwE10mv4r3OQlyUZlsETyUhMohKf2gZQmMN--MrTmaU4WnB1RW4yLxjEs43LUptOW2ybGv-xGu-Cmv6EP7QY3ouwhD1A_9uOZyDfsPpyQj0D3_6p0FTUtMDnlM8Mw4qd3haA1pCTzEfvEmRrHjDvuwVhUe0ingnknCUPuiyNdKfFedG7VbqYfUgb3B3u9nm95v8YXN3U-_Uulxt7kq1qgqlNtuiEgWu76u7aouivJd4o3ciF6t8nW_yTS5W98vborxTeLspsBB31cN9ts6xkdosjXlrmNRvNFGLu83tw_3qxsgCDcUHnEJY7CB-yMvx3ecbv-PvLIr2QNk6N5oCTVaCDgZ3LzOonIiYd_hho4wMbtUPWN7GYe2rq529ab3ZXegRHeq2WJauycSer-9_LY7e_YVlyMQ-Ok2Z2Meg_hMAAP__D6E4jg">