Skip to content

Commit 15c9a68

Browse files
committed
Add float quickcheck
1 parent 600ed3b commit 15c9a68

File tree

3 files changed

+129
-100
lines changed

3 files changed

+129
-100
lines changed

src/float/add.rs

+16-99
Original file line numberDiff line numberDiff line change
@@ -187,111 +187,28 @@ add!(__adddf3: f64);
187187
#[cfg(test)]
188188
mod tests {
189189
use core::{f32, f64};
190+
use qc::{F32, F64};
190191

191-
use float::{Float, FRepr};
192-
use qc::{U32, U64};
193-
194-
// TODO: Add F32/F64 to qc so that they print the right values (at the very least)
195192
check! {
196193
fn __addsf3(f: extern fn(f32, f32) -> f32,
197-
a: U32,
198-
b: U32)
199-
-> Option<FRepr<f32> > {
200-
let (a, b) = (f32::from_repr(a.0), f32::from_repr(b.0));
201-
Some(FRepr(f(a, b)))
194+
a: F32,
195+
b: F32)
196+
-> Option<F32> {
197+
if option_env!("TARGET") != Some("arm-unknown-linux-gnueabi") {
198+
Some(F32(f(a.0, b.0)))
199+
} else {
200+
None
201+
}
202202
}
203203

204204
fn __adddf3(f: extern fn(f64, f64) -> f64,
205-
a: U64,
206-
b: U64) -> Option<FRepr<f64> > {
207-
let (a, b) = (f64::from_repr(a.0), f64::from_repr(b.0));
208-
Some(FRepr(f(a, b)))
205+
a: F64,
206+
b: F64) -> Option<F64> {
207+
if option_env!("TARGET") != Some("arm-unknown-linux-gnueabi") {
208+
Some(F64(f(a.0, b.0)))
209+
} else {
210+
None
211+
}
209212
}
210213
}
211-
212-
// More tests for special float values
213-
214-
#[test]
215-
fn test_float_tiny_plus_tiny() {
216-
let tiny = f32::from_repr(1);
217-
let r = super::__addsf3(tiny, tiny);
218-
assert!(r.eq_repr(tiny + tiny));
219-
}
220-
221-
#[test]
222-
fn test_double_tiny_plus_tiny() {
223-
let tiny = f64::from_repr(1);
224-
let r = super::__adddf3(tiny, tiny);
225-
assert!(r.eq_repr(tiny + tiny));
226-
}
227-
228-
#[test]
229-
fn test_float_small_plus_small() {
230-
let a = f32::from_repr(327);
231-
let b = f32::from_repr(256);
232-
let r = super::__addsf3(a, b);
233-
assert!(r.eq_repr(a + b));
234-
}
235-
236-
#[test]
237-
fn test_double_small_plus_small() {
238-
let a = f64::from_repr(327);
239-
let b = f64::from_repr(256);
240-
let r = super::__adddf3(a, b);
241-
assert!(r.eq_repr(a + b));
242-
}
243-
244-
#[test]
245-
fn test_float_one_plus_one() {
246-
let r = super::__addsf3(1f32, 1f32);
247-
assert!(r.eq_repr(1f32 + 1f32));
248-
}
249-
250-
#[test]
251-
fn test_double_one_plus_one() {
252-
let r = super::__adddf3(1f64, 1f64);
253-
assert!(r.eq_repr(1f64 + 1f64));
254-
}
255-
256-
#[test]
257-
fn test_float_different_nan() {
258-
let a = f32::from_repr(1);
259-
let b = f32::from_repr(0b11111111100100010001001010101010);
260-
let x = super::__addsf3(a, b);
261-
let y = a + b;
262-
assert!(x.eq_repr(y));
263-
}
264-
265-
#[test]
266-
fn test_double_different_nan() {
267-
let a = f64::from_repr(1);
268-
let b = f64::from_repr(0b1111111111110010001000100101010101001000101010000110100011101011);
269-
let x = super::__adddf3(a, b);
270-
let y = a + b;
271-
assert!(x.eq_repr(y));
272-
}
273-
274-
#[test]
275-
fn test_float_nan() {
276-
let r = super::__addsf3(f32::NAN, 1.23);
277-
assert_eq!(r.repr(), f32::NAN.repr());
278-
}
279-
280-
#[test]
281-
fn test_double_nan() {
282-
let r = super::__adddf3(f64::NAN, 1.23);
283-
assert_eq!(r.repr(), f64::NAN.repr());
284-
}
285-
286-
#[test]
287-
fn test_float_inf() {
288-
let r = super::__addsf3(f32::INFINITY, -123.4);
289-
assert_eq!(r, f32::INFINITY);
290-
}
291-
292-
#[test]
293-
fn test_double_inf() {
294-
let r = super::__adddf3(f64::INFINITY, -123.4);
295-
assert_eq!(r, f64::INFINITY);
296-
}
297214
}

src/float/mod.rs

+48-1
Original file line numberDiff line numberDiff line change
@@ -16,22 +16,41 @@ pub trait Float: Sized + Copy {
1616
/// Returns the bitwidth of the significand
1717
fn significand_bits() -> u32;
1818

19+
/// Returns the bitwidth of the exponent
20+
fn exponent_bits() -> u32 {
21+
Self::bits() - Self::significand_bits() - 1
22+
}
23+
24+
/// Returns a mask for the sign bit
25+
fn sign_mask() -> Self::Int;
26+
27+
/// Returns a mask for the significand
28+
fn significand_mask() -> Self::Int;
29+
30+
/// Returns a mask for the exponent
31+
fn exponent_mask() -> Self::Int;
32+
1933
/// Returns `self` transmuted to `Self::Int`
2034
fn repr(self) -> Self::Int;
2135

2236
#[cfg(test)]
2337
/// Checks if two floats have the same bit representation. *Except* for NaNs! NaN can be
24-
/// represented in multiple different ways. This methods returns `true` if two NaNs are
38+
/// represented in multiple different ways. This method returns `true` if two NaNs are
2539
/// compared.
2640
fn eq_repr(self, rhs: Self) -> bool;
2741

2842
/// Returns a `Self::Int` transmuted back to `Self`
2943
fn from_repr(a: Self::Int) -> Self;
3044

45+
/// Constructs a `Self` from its parts. Inputs are treated as bits and shifted into position.
46+
fn from_parts(sign: bool, exponent: Self::Int, significand: Self::Int) -> Self;
47+
3148
/// Returns (normalized exponent, normalized significand)
3249
fn normalize(significand: Self::Int) -> (i32, Self::Int);
3350
}
3451

52+
// FIXME: Some of this can be removed if RFC Issue #1424 is resolved
53+
// https://github.com/rust-lang/rfcs/issues/1424
3554
impl Float for f32 {
3655
type Int = u32;
3756
fn bits() -> u32 {
@@ -40,6 +59,15 @@ impl Float for f32 {
4059
fn significand_bits() -> u32 {
4160
23
4261
}
62+
fn sign_mask() -> Self::Int {
63+
1 << (Self::bits() - 1)
64+
}
65+
fn significand_mask() -> Self::Int {
66+
(1 << Self::significand_bits()) - 1
67+
}
68+
fn exponent_mask() -> Self::Int {
69+
!(Self::sign_mask() | Self::significand_mask())
70+
}
4371
fn repr(self) -> Self::Int {
4472
unsafe { mem::transmute(self) }
4573
}
@@ -54,6 +82,11 @@ impl Float for f32 {
5482
fn from_repr(a: Self::Int) -> Self {
5583
unsafe { mem::transmute(a) }
5684
}
85+
fn from_parts(sign: bool, exponent: Self::Int, significand: Self::Int) -> Self {
86+
Self::from_repr(((sign as Self::Int) << (Self::bits() - 1)) |
87+
((exponent << Self::significand_bits()) & Self::exponent_mask()) |
88+
(significand & Self::significand_mask()))
89+
}
5790
fn normalize(significand: Self::Int) -> (i32, Self::Int) {
5891
let shift = significand.leading_zeros()
5992
.wrapping_sub((1u32 << Self::significand_bits()).leading_zeros());
@@ -68,6 +101,15 @@ impl Float for f64 {
68101
fn significand_bits() -> u32 {
69102
52
70103
}
104+
fn sign_mask() -> Self::Int {
105+
1 << (Self::bits() - 1)
106+
}
107+
fn significand_mask() -> Self::Int {
108+
(1 << Self::significand_bits()) - 1
109+
}
110+
fn exponent_mask() -> Self::Int {
111+
!(Self::sign_mask() | Self::significand_mask())
112+
}
71113
fn repr(self) -> Self::Int {
72114
unsafe { mem::transmute(self) }
73115
}
@@ -82,6 +124,11 @@ impl Float for f64 {
82124
fn from_repr(a: Self::Int) -> Self {
83125
unsafe { mem::transmute(a) }
84126
}
127+
fn from_parts(sign: bool, exponent: Self::Int, significand: Self::Int) -> Self {
128+
Self::from_repr(((sign as Self::Int) << (Self::bits() - 1)) |
129+
((exponent << Self::significand_bits()) & Self::exponent_mask()) |
130+
(significand & Self::significand_mask()))
131+
}
85132
fn normalize(significand: Self::Int) -> (i32, Self::Int) {
86133
let shift = significand.leading_zeros()
87134
.wrapping_sub((1u64 << Self::significand_bits()).leading_zeros());

src/qc.rs

+65
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@
55

66
use std::boxed::Box;
77
use std::fmt;
8+
use core::{f32, f64};
89

910
use quickcheck::{Arbitrary, Gen};
1011

1112
use int::LargeInt;
13+
use float::Float;
1214

1315
// Generates values in the full range of the integer type
1416
macro_rules! arbitrary {
@@ -143,6 +145,69 @@ macro_rules! arbitrary_large {
143145
arbitrary_large!(I64: i64);
144146
arbitrary_large!(U64: u64);
145147

148+
macro_rules! arbitrary_float {
149+
($TY:ident : $ty:ident) => {
150+
#[derive(Clone, Copy)]
151+
pub struct $TY(pub $ty);
152+
153+
impl Arbitrary for $TY {
154+
fn arbitrary<G>(g: &mut G) -> $TY
155+
where G: Gen
156+
{
157+
let special = [
158+
-0.0, 0.0, $ty::NAN, $ty::INFINITY, -$ty::INFINITY
159+
];
160+
161+
if g.gen_weighted_bool(10) { // Random special case
162+
$TY(*g.choose(&special).unwrap())
163+
} else if g.gen_weighted_bool(10) { // NaN variants
164+
let sign: bool = g.gen();
165+
let exponent: <$ty as Float>::Int = g.gen();
166+
let significand: <$ty as Float>::Int = 0;
167+
$TY($ty::from_parts(sign, exponent, significand))
168+
} else if g.gen() { // Denormalized
169+
let sign: bool = g.gen();
170+
let exponent: <$ty as Float>::Int = 0;
171+
let significand: <$ty as Float>::Int = g.gen();
172+
$TY($ty::from_parts(sign, exponent, significand))
173+
} else { // Random anything
174+
let sign: bool = g.gen();
175+
let exponent: <$ty as Float>::Int = g.gen();
176+
let significand: <$ty as Float>::Int = g.gen();
177+
$TY($ty::from_parts(sign, exponent, significand))
178+
}
179+
}
180+
181+
fn shrink(&self) -> Box<Iterator<Item=$TY>> {
182+
::quickcheck::empty_shrinker()
183+
}
184+
}
185+
186+
impl fmt::Debug for $TY {
187+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
188+
fmt::Debug::fmt(&self.0, f)
189+
}
190+
}
191+
192+
impl PartialEq for $TY {
193+
fn eq(&self, other: &$TY) -> bool {
194+
// NOTE(cfg) for some reason, on hard float targets, our implementation doesn't
195+
// match the output of its gcc_s counterpart. Until we investigate further, we'll
196+
// just avoid testing against gcc_s on those targets. Do note that our
197+
// implementation matches the output of the FPU instruction on *hard* float targets
198+
// and matches its gcc_s counterpart on *soft* float targets.
199+
if cfg!(gnueabihf) {
200+
return true
201+
}
202+
self.0.eq_repr(other.0)
203+
}
204+
}
205+
}
206+
}
207+
208+
arbitrary_float!(F32: f32);
209+
arbitrary_float!(F64: f64);
210+
146211
// Convenience macro to test intrinsics against their reference implementations.
147212
//
148213
// Each intrinsic is tested against both the `gcc_s` library as well as

0 commit comments

Comments
 (0)