Skip to content

min_element / max_element produce incorrect results for NaNs in the last place #5

@gnzlbg

Description

@gnzlbg

See it live: https://play.rust-lang.org/?gist=2ccd8fb4e41fc1b28a1cc6a2e5774a7b&version=nightly

#![feature(repr_simd)]
#![feature(platform_intrinsics)]

extern "platform-intrinsic" {
    fn simd_reduce_min<T,U>(a: T) -> U;
}

#[repr(simd)]
pub struct F(f32, f32);

pub fn foo(x: F) -> f32 {
   unsafe { simd_reduce_min(x) }
}

fn main() {
 let x = F(1.0, -1.0);
 assert_eq!(foo(x), -1.0);   // OK
 let y = F(std::f32::NAN, -1.0);
 assert_eq!(foo(y), -1.0);   // OK
 let z = F(-1.0, std::f32::NAN);
 assert_eq!(foo(z), -1.0);  // FAILS: returns NaN
}

So I've filled: https://bugs.llvm.org/show_bug.cgi?id=36982

This issue is probably because llvm.vector.reduce.fmin/fmax have the semantics of fcmp instead of minnum/maxnum. We will probably need to workaround this issue in stdsimd.

What we want is the same semantics that Rust specifies for min/max on floating-point primitive types. For min this is:

Returns the minimum of the two numbers. If one of the arguments is NaN, then the other argument is returned.

This looks compatible with the specifications of IEEE754-2018 (drafts are here: http://754r.ucbtest.org/drafts/). From the latest draft (P754-233 17-Mar-2018):

**minimumNumber**(x, y) is x if x<y, y if y<x, and the number if one operand is a number and the
other is a NaN. For this operation, −0 compares less than +0. If x=y and signs are the same it is
either x or y. If both operands are NaNs, a quiet NaN is returned, according to 6.2. If either
operand is a signaling NaN, an invalid operation exception is signaled, but unless both operands
are NaNs, the signaling NaN is otherwise ignored and not converted to a quiet NaN as stated in
6.2 for other operations.

**maximumNumber**(x, y) is x if x>y, y if y>x, and the number if one operand is a number and the
other is a NaN. For this operation, +0 compares greater than −0. If x=y and signs are the same it
is either x or y.If both operands are NaNs, a quiet NaN is returned, according to 6.2. If either
operand is a signaling NaN, an invalid operation exception is signaled, but unless both operands
are NaNs, the signaling NaN is otherwise ignored and not converted to a quiet NaN as stated in
6.2 for other operations

Metadata

Metadata

Assignees

No one assigned

    Labels

    UnsoundSomething breaks Rust safety guarantees

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions