Skip to content

Divergence in floating point behaviour between Unix and MacOS builds #59095

@robhowarth

Description

@robhowarth

Version

v22.10.0

Platform

Darwin RH-MacBook-SpaceGrey.cable.virginm.net 24.5.0 Darwin Kernel Version 24.5.0: Tue Apr 22 19:54:49 PDT 2025; root:xnu-11417.121.6~2/RELEASE_ARM64_T6000 arm64

Subsystem

No response

What steps will reproduce the bug?

run the following on v22.10.0 on MacOS and on a Unix build (eg x86_64)

const val = 0.0366
console.log(Math.acosh(Math.cosh(val)) - val)

How often does it reproduce? Is there a required condition?

Always

What is the expected behavior? Why is that the expected behavior?

I expected the floating point behaviour be consistent between different platforms.

What do you see instead?

The floating point behaviour has diverged from v22.10.0 onwards.

Using the stock builds for v22.10.0 I see:

  • Unix: 2.921274333544943e-15
  • MacOS: 2.9282132274488504e-15

The output is also different between v22.9.0 and v22.10.0 on MacOS, whereas it's consistent from v22.9.0 to v22.10.0 on the stock Unix builds.

Additional information

I think this is a result of the default for the ffp-contract flag changing to on between clang 13 and clang 14 (release notes).

The binaries for MacOS for v22.9.0 to v22.10.0 appear to have been built with different versions of XCode. If you search for xcode_version in the binaries it looks like v22.9.0 was built with xcode 13 (and therefore clang 13), while v22.10.0 was built with xcode 14 (and therefore clang 14).

Building on MacOS with and without CXXFLAGS="-ffp-contract=off" and running the Javascript above is another way to see the difference in behaviour.

In deps/v8/build.gn it looks like ffp-contract is explicitly set to off for some architectures (eg ppc64). So it looks like there's some precedent for overriding default settings for ffp-contract. This file appears to have come from the v8 project though.

The code above with Math.cosh and Math.acosh was just the shortest example I could produce. It seems likely other functions under Math are impacted given the compiler output is different for several functions in ieee754.cc. There may be impacts outside of these functions also as the binary output for other modules is also different, but I wasn't able to find a short example to reproduce those differences if so.

Reading around it seems that other projects have been impacted by the divergence between gcc and clang in terms of the ffp-contract flag, and a range of approaches have been taken. So appreciate it's perhaps not clear if this should be "fixed". Also, it's possible this is question for the v8 project rather than nodejs given it looks like there's already been some activity around this flag there. That said, the behaviour has changed between v22.9.0 and v22.10.0 in the binaries available from nodejs.org so posting here to start.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions