Skip to content

f*::min/max do not behave as documented for signaling NaN on aarch64, or with optimizations #149537

@RalfJung

Description

@RalfJung

View all comments

Our documentation says that max(SNaN, 0.0) should return 0.0. On x86_64, this is indeed what happens. However, on aarch64, we get a QNaN as a result instead. On Android, the reported result for this test is:

F32::from(F32::nan(Sign::Pos, NaNKind::Signaling, 1).as_f32().max(0.0)) = 0x7fc00001 (NaN: Pos, Quiet, payload = 0x1)

It seems that LLVM never actually correctly implemented the promise that "if exactly one argument is NaN, the other argument is returned". Recently things got a lot more messy (see llvm/llvm-project#170082 and also this discussion), but apparently things were already broken before this recent chaos.

Also, since Rust 1.91 optimizations seem to fold max(SNaN, x) into NaN.

It is also worth noting that glibc fmin/fmax were deliberately patched so that SNaN inputs produce NaN results. (However, not all libc do that; the C standard generally says very little about SNaN.)

So it might just be worth dropping that promise and allowing NaN outputs when there is an SNaN input (but also continue to allow returning the other input).

Cc @tgross35 @valadaptive @nikic @workingjubilee @rust-lang/libs-api

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-LLVMArea: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues.A-floating-pointArea: Floating point numbers and arithmeticC-bugCategory: This is a bug.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.T-libs-apiRelevant to the library API team, which will review and decide on the PR/issue.

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions