From 07b446a9ab854b32a2cbb2ee04d0ba6ce46448ee Mon Sep 17 00:00:00 2001 From: whitequark Date: Fri, 29 Dec 2017 07:49:45 +0000 Subject: [PATCH] comparesf2/comparedf2: fix a signedness bug and add tests. --- build.rs | 318 +++++++++++++++++++++++++++++++++++++++++++++++ src/float/cmp.rs | 28 +++-- src/float/mod.rs | 16 ++- tests/gedf2.rs | 7 ++ tests/gesf2.rs | 7 ++ tests/ledf2.rs | 7 ++ tests/lesf2.rs | 7 ++ 7 files changed, 376 insertions(+), 14 deletions(-) create mode 100644 tests/gedf2.rs create mode 100644 tests/gesf2.rs create mode 100644 tests/ledf2.rs create mode 100644 tests/lesf2.rs diff --git a/build.rs b/build.rs index a711392..0a12a75 100644 --- a/build.rs +++ b/build.rs @@ -89,6 +89,12 @@ mod tests { Adddf3, Addsf3, + // float/cmp.rs + Gedf2, + Gesf2, + Ledf2, + Lesf2, + // float/conv.rs Fixdfdi, Fixdfsi, @@ -2529,6 +2535,318 @@ fn floatuntidf() { } } + #[derive(Eq, Hash, PartialEq)] + pub struct Gedf2 { + a: u64, + b: u64, + c: i32, + } + + impl TestCase for Gedf2 { + fn name() -> &'static str { + "gedf2" + } + + fn generate(rng: &mut R) -> Option + where + R: Rng, + Self: Sized, + { + let a = gen_f64(rng); + let b = gen_f64(rng); + // TODO accept NaNs. We don't do that right now because we can't check + // for NaN-ness on the thumb targets (due to missing intrinsics) + if a.is_nan() || b.is_nan() { + return None; + } + + let c; + if a.is_nan() || b.is_nan() { + c = -1; + } else if a < b { + c = -1; + } else if a > b { + c = 1; + } else { + c = 0; + } + + Some(Gedf2 { a: to_u64(a), b: to_u64(b), c }) + } + + fn to_string(&self, buffer: &mut String) { + writeln!( + buffer, + "(({a}, {b}), {c}),", + a = self.a, + b = self.b, + c = self.c + ) + .unwrap(); + } + + fn prologue() -> &'static str { + " +use std::mem; +use compiler_builtins::float::cmp::__gedf2; + +fn to_f64(x: u64) -> f64 { + unsafe { mem::transmute(x) } +} + +static TEST_CASES: &[((u64, u64), i32)] = &[ +" + } + + fn epilogue() -> &'static str { + " +]; + +#[test] +fn gedf2() { + for &((a, b), c) in TEST_CASES { + let c_ = __gedf2(to_f64(a), to_f64(b)); + assert_eq!(((a, b), c), ((a, b), c_)); + } +} +" + } + } + + #[derive(Eq, Hash, PartialEq)] + pub struct Gesf2 { + a: u32, + b: u32, + c: i32, + } + + impl TestCase for Gesf2 { + fn name() -> &'static str { + "gesf2" + } + + fn generate(rng: &mut R) -> Option + where + R: Rng, + Self: Sized, + { + let a = gen_f32(rng); + let b = gen_f32(rng); + // TODO accept NaNs. We don't do that right now because we can't check + // for NaN-ness on the thumb targets (due to missing intrinsics) + if a.is_nan() || b.is_nan() { + return None; + } + + let c; + if a.is_nan() || b.is_nan() { + c = -1; + } else if a < b { + c = -1; + } else if a > b { + c = 1; + } else { + c = 0; + } + + Some(Gesf2 { a: to_u32(a), b: to_u32(b), c }) + } + + fn to_string(&self, buffer: &mut String) { + writeln!( + buffer, + "(({a}, {b}), {c}),", + a = self.a, + b = self.b, + c = self.c + ) + .unwrap(); + } + + fn prologue() -> &'static str { + " +use std::mem; +use compiler_builtins::float::cmp::__gesf2; + +fn to_f32(x: u32) -> f32 { + unsafe { mem::transmute(x) } +} + +static TEST_CASES: &[((u32, u32), i32)] = &[ +" + } + + fn epilogue() -> &'static str { + " +]; + +#[test] +fn gesf2() { + for &((a, b), c) in TEST_CASES { + let c_ = __gesf2(to_f32(a), to_f32(b)); + assert_eq!(((a, b), c), ((a, b), c_)); + } +} +" + } + } + + #[derive(Eq, Hash, PartialEq)] + pub struct Ledf2 { + a: u64, + b: u64, + c: i32, + } + + impl TestCase for Ledf2 { + fn name() -> &'static str { + "ledf2" + } + + fn generate(rng: &mut R) -> Option + where + R: Rng, + Self: Sized, + { + let a = gen_f64(rng); + let b = gen_f64(rng); + // TODO accept NaNs. We don't do that right now because we can't check + // for NaN-ness on the thumb targets (due to missing intrinsics) + if a.is_nan() || b.is_nan() { + return None; + } + + let c; + if a.is_nan() || b.is_nan() { + c = 1; + } else if a < b { + c = -1; + } else if a > b { + c = 1; + } else { + c = 0; + } + + Some(Ledf2 { a: to_u64(a), b: to_u64(b), c }) + } + + fn to_string(&self, buffer: &mut String) { + writeln!( + buffer, + "(({a}, {b}), {c}),", + a = self.a, + b = self.b, + c = self.c + ) + .unwrap(); + } + + fn prologue() -> &'static str { + " +use std::mem; +use compiler_builtins::float::cmp::__ledf2; + +fn to_f64(x: u64) -> f64 { + unsafe { mem::transmute(x) } +} + +static TEST_CASES: &[((u64, u64), i32)] = &[ +" + } + + fn epilogue() -> &'static str { + " +]; + +#[test] +fn ledf2() { + for &((a, b), c) in TEST_CASES { + let c_ = __ledf2(to_f64(a), to_f64(b)); + assert_eq!(((a, b), c), ((a, b), c_)); + } +} +" + } + } + + #[derive(Eq, Hash, PartialEq)] + pub struct Lesf2 { + a: u32, + b: u32, + c: i32, + } + + impl TestCase for Lesf2 { + fn name() -> &'static str { + "lesf2" + } + + fn generate(rng: &mut R) -> Option + where + R: Rng, + Self: Sized, + { + let a = gen_f32(rng); + let b = gen_f32(rng); + // TODO accept NaNs. We don't do that right now because we can't check + // for NaN-ness on the thumb targets (due to missing intrinsics) + if a.is_nan() || b.is_nan() { + return None; + } + + let c; + if a.is_nan() || b.is_nan() { + c = 1; + } else if a < b { + c = -1; + } else if a > b { + c = 1; + } else { + c = 0; + } + + Some(Lesf2 { a: to_u32(a), b: to_u32(b), c }) + } + + fn to_string(&self, buffer: &mut String) { + writeln!( + buffer, + "(({a}, {b}), {c}),", + a = self.a, + b = self.b, + c = self.c + ) + .unwrap(); + } + + fn prologue() -> &'static str { + " +use std::mem; +use compiler_builtins::float::cmp::__lesf2; + +fn to_f32(x: u32) -> f32 { + unsafe { mem::transmute(x) } +} + +static TEST_CASES: &[((u32, u32), i32)] = &[ +" + } + + fn epilogue() -> &'static str { + " +]; + +#[test] +fn lesf2() { + for &((a, b), c) in TEST_CASES { + let c_ = __lesf2(to_f32(a), to_f32(b)); + assert_eq!(((a, b), c), ((a, b), c_)); + } +} +" + } + } + #[derive(Eq, Hash, PartialEq)] pub struct Moddi3 { a: i64, diff --git a/src/float/cmp.rs b/src/float/cmp.rs index 2434deb..e884872 100644 --- a/src/float/cmp.rs +++ b/src/float/cmp.rs @@ -1,3 +1,5 @@ +#![allow(unreachable_code)] + use int::{Int, CastInto}; use float::Float; @@ -35,18 +37,19 @@ fn cmp(a: F, b: F) -> Result where i32: CastInto, F::Int: CastInto, { - let one = F::Int::ONE; - let zero = F::Int::ZERO; + let one = F::Int::ONE; + let zero = F::Int::ZERO; + let szero = F::SignedInt::ZERO; let sign_bit = F::SIGN_MASK as F::Int; let abs_mask = sign_bit - one; let exponent_mask = F::EXPONENT_MASK; let inf_rep = exponent_mask; - let a_rep = a.repr(); - let b_rep = b.repr(); - let a_abs = a_rep & abs_mask; - let b_abs = b_rep & abs_mask; + let a_rep = a.repr(); + let b_rep = b.repr(); + let a_abs = a_rep & abs_mask; + let b_abs = b_rep & abs_mask; // If either a or b is NaN, they are unordered. if a_abs > inf_rep || b_abs > inf_rep { @@ -58,12 +61,15 @@ fn cmp(a: F, b: F) -> Result where return Result::Equal } + let a_srep = a.signed_repr(); + let b_srep = b.signed_repr(); + // If at least one of a and b is positive, we get the same result comparing // a and b as signed integers as we would with a fp_ting-point compare. - if a_rep & b_rep >= zero { - if a_rep < b_rep { + if a_srep & b_srep >= szero { + if a_srep < b_srep { return Result::Less - } else if a_rep == b_rep { + } else if a_srep == b_srep { return Result::Equal } else { return Result::Greater @@ -75,9 +81,9 @@ fn cmp(a: F, b: F) -> Result where // complement integer representation; if integers are represented in a // sign-magnitude representation, then this flip is incorrect). else { - if a_rep > b_rep { + if a_srep > b_srep { return Result::Less - } else if a_rep == b_rep { + } else if a_srep == b_srep { return Result::Equal } else { return Result::Greater diff --git a/src/float/mod.rs b/src/float/mod.rs index f13256d..51ed667 100644 --- a/src/float/mod.rs +++ b/src/float/mod.rs @@ -26,6 +26,9 @@ pub trait Float: /// A uint of the same with as the float type Int: Int; + /// A int of the same with as the float + type SignedInt: Int; + const ZERO: Self; const ONE: Self; @@ -59,6 +62,9 @@ pub trait Float: /// Returns `self` transmuted to `Self::Int` fn repr(self) -> Self::Int; + /// Returns `self` transmuted to `Self::SignedInt` + fn signed_repr(self) -> Self::SignedInt; + #[cfg(test)] /// Checks if two floats have the same bit representation. *Except* for NaNs! NaN can be /// represented in multiple different ways. This method returns `true` if two NaNs are @@ -78,9 +84,10 @@ pub trait Float: // FIXME: Some of this can be removed if RFC Issue #1424 is resolved // https://github.com/rust-lang/rfcs/issues/1424 macro_rules! float_impl { - ($ty:ident, $ity:ident, $bits:expr, $significand_bits:expr) => { + ($ty:ident, $ity:ident, $sity:ident, $bits:expr, $significand_bits:expr) => { impl Float for $ty { type Int = $ity; + type SignedInt = $sity; const ZERO: Self = 0.0; const ONE: Self = 1.0; @@ -95,6 +102,9 @@ macro_rules! float_impl { fn repr(self) -> Self::Int { unsafe { mem::transmute(self) } } + fn signed_repr(self) -> Self::SignedInt { + unsafe { mem::transmute(self) } + } #[cfg(test)] fn eq_repr(self, rhs: Self) -> bool { if self.is_nan() && rhs.is_nan() { @@ -120,5 +130,5 @@ macro_rules! float_impl { } } -float_impl!(f32, u32, 32, 23); -float_impl!(f64, u64, 64, 52); +float_impl!(f32, u32, i32, 32, 23); +float_impl!(f64, u64, i64, 64, 52); diff --git a/tests/gedf2.rs b/tests/gedf2.rs new file mode 100644 index 0000000..e1bea59 --- /dev/null +++ b/tests/gedf2.rs @@ -0,0 +1,7 @@ +#![feature(compiler_builtins_lib)] +#![feature(i128_type)] +#![cfg_attr(all(target_arch = "arm", not(any(target_env = "gnu", target_env = "musl")), + target_os = "linux", test), + no_std)] + +include!(concat!(env!("OUT_DIR"), "/gedf2.rs")); diff --git a/tests/gesf2.rs b/tests/gesf2.rs new file mode 100644 index 0000000..760f5a8 --- /dev/null +++ b/tests/gesf2.rs @@ -0,0 +1,7 @@ +#![feature(compiler_builtins_lib)] +#![feature(i128_type)] +#![cfg_attr(all(target_arch = "arm", not(any(target_env = "gnu", target_env = "musl")), + target_os = "linux", test), + no_std)] + +include!(concat!(env!("OUT_DIR"), "/gesf2.rs")); diff --git a/tests/ledf2.rs b/tests/ledf2.rs new file mode 100644 index 0000000..3fac17b --- /dev/null +++ b/tests/ledf2.rs @@ -0,0 +1,7 @@ +#![feature(compiler_builtins_lib)] +#![feature(i128_type)] +#![cfg_attr(all(target_arch = "arm", not(any(target_env = "gnu", target_env = "musl")), + target_os = "linux", test), + no_std)] + +include!(concat!(env!("OUT_DIR"), "/ledf2.rs")); diff --git a/tests/lesf2.rs b/tests/lesf2.rs new file mode 100644 index 0000000..878861f --- /dev/null +++ b/tests/lesf2.rs @@ -0,0 +1,7 @@ +#![feature(compiler_builtins_lib)] +#![feature(i128_type)] +#![cfg_attr(all(target_arch = "arm", not(any(target_env = "gnu", target_env = "musl")), + target_os = "linux", test), + no_std)] + +include!(concat!(env!("OUT_DIR"), "/lesf2.rs"));