comparesf2/comparedf2: fix a signedness bug and add tests.

This commit is contained in:
whitequark
2017-12-29 07:49:45 +00:00
parent 45112fc65f
commit 566e7ede90
7 changed files with 376 additions and 14 deletions

View File

@@ -1,3 +1,5 @@
#![allow(unreachable_code)]
use int::{Int, CastInto};
use float::Float;
@@ -35,18 +37,19 @@ fn cmp<F: Float>(a: F, b: F) -> Result where
i32: CastInto<F::Int>,
F::Int: CastInto<i32>,
{
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<F: Float>(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<F: Float>(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

View File

@@ -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);