Skip to content

Commit c0afae5

Browse files
committed
Add Quickcheck types for float tests
1 parent a52d73e commit c0afae5

File tree

3 files changed

+119
-123
lines changed

3 files changed

+119
-123
lines changed

src/float/add.rs

+10-105
Original file line numberDiff line numberDiff line change
@@ -89,12 +89,12 @@ macro_rules! add {
8989
if a_exponent.0 == 0 {
9090
let (exponent, significand) = <$ty>::normalize(a_significand.0);
9191
a_exponent = Wrapping(exponent);
92-
a_significand = Wrapping(significand);
92+
a_significand = Wrapping(significand);
9393
}
9494
if b_exponent.0 == 0 {
9595
let (exponent, significand) = <$ty>::normalize(b_significand.0);
9696
b_exponent = Wrapping(exponent);
97-
b_significand = Wrapping(significand);
97+
b_significand = Wrapping(significand);
9898
}
9999

100100
// The sign of the result is the sign of the larger operand, a. If they
@@ -123,8 +123,8 @@ macro_rules! add {
123123
if subtraction {
124124
a_significand -= b_significand;
125125
// If a == -b, return +zero.
126-
if a_significand.0 == 0 {
127-
return (<$ty as Float>::from_repr(0));
126+
if a_significand.0 == 0 {
127+
return (<$ty as Float>::from_repr(0));
128128
}
129129

130130
// If partial cancellation occured, we need to left-shift the result
@@ -148,7 +148,7 @@ macro_rules! add {
148148
}
149149

150150
// If we have overflowed the type, return +/- infinity:
151-
if a_exponent >= Wrapping(max_exponent.0 as i32) {
151+
if a_exponent >= Wrapping(max_exponent.0 as i32) {
152152
return (<$ty>::from_repr((inf_rep | result_sign).0));
153153
}
154154

@@ -198,117 +198,22 @@ pub extern fn __aeabi_fadd(a: f32, b: f32) -> f32 {
198198

199199
#[cfg(test)]
200200
mod tests {
201-
use core::{f32, f64};
202-
use qc::{U32, U64};
203201
use float::Float;
202+
use qc::{F32, F64};
204203

205-
// NOTE The tests below have special handing for NaN values.
206-
// Because NaN != NaN, the floating-point representations must be used
207-
// Because there are many diffferent values of NaN, and the implementation
208-
// doesn't care about calculating the 'correct' one, if both values are NaN
209-
// the values are considered equivalent.
210-
211-
// TODO: Add F32/F64 to qc so that they print the right values (at the very least)
212204
quickcheck! {
213-
fn addsf3(a: U32, b: U32) -> bool {
214-
let (a, b) = (f32::from_repr(a.0), f32::from_repr(b.0));
205+
fn addsf3(a: F32, b: F32) -> bool {
206+
let (a, b) = (a.0, b.0);
215207
let x = super::__addsf3(a, b);
216208
let y = a + b;
217209
x.eq_repr(y)
218210
}
219211

220-
fn adddf3(a: U64, b: U64) -> bool {
221-
let (a, b) = (f64::from_repr(a.0), f64::from_repr(b.0));
212+
fn adddf3(a: F64, b: F64) -> bool {
213+
let (a, b) = (a.0, b.0);
222214
let x = super::__adddf3(a, b);
223215
let y = a + b;
224216
x.eq_repr(y)
225217
}
226218
}
227-
228-
// More tests for special float values
229-
230-
#[test]
231-
fn test_float_tiny_plus_tiny() {
232-
let tiny = f32::from_repr(1);
233-
let r = super::__addsf3(tiny, tiny);
234-
assert!(r.eq_repr(tiny + tiny));
235-
}
236-
237-
#[test]
238-
fn test_double_tiny_plus_tiny() {
239-
let tiny = f64::from_repr(1);
240-
let r = super::__adddf3(tiny, tiny);
241-
assert!(r.eq_repr(tiny + tiny));
242-
}
243-
244-
#[test]
245-
fn test_float_small_plus_small() {
246-
let a = f32::from_repr(327);
247-
let b = f32::from_repr(256);
248-
let r = super::__addsf3(a, b);
249-
assert!(r.eq_repr(a + b));
250-
}
251-
252-
#[test]
253-
fn test_double_small_plus_small() {
254-
let a = f64::from_repr(327);
255-
let b = f64::from_repr(256);
256-
let r = super::__adddf3(a, b);
257-
assert!(r.eq_repr(a + b));
258-
}
259-
260-
#[test]
261-
fn test_float_one_plus_one() {
262-
let r = super::__addsf3(1f32, 1f32);
263-
assert!(r.eq_repr(1f32 + 1f32));
264-
}
265-
266-
#[test]
267-
fn test_double_one_plus_one() {
268-
let r = super::__adddf3(1f64, 1f64);
269-
assert!(r.eq_repr(1f64 + 1f64));
270-
}
271-
272-
#[test]
273-
fn test_float_different_nan() {
274-
let a = f32::from_repr(1);
275-
let b = f32::from_repr(0b11111111100100010001001010101010);
276-
let x = super::__addsf3(a, b);
277-
let y = a + b;
278-
assert!(x.eq_repr(y));
279-
}
280-
281-
#[test]
282-
fn test_double_different_nan() {
283-
let a = f64::from_repr(1);
284-
let b = f64::from_repr(
285-
0b1111111111110010001000100101010101001000101010000110100011101011);
286-
let x = super::__adddf3(a, b);
287-
let y = a + b;
288-
assert!(x.eq_repr(y));
289-
}
290-
291-
#[test]
292-
fn test_float_nan() {
293-
let r = super::__addsf3(f32::NAN, 1.23);
294-
assert_eq!(r.repr(), f32::NAN.repr());
295-
}
296-
297-
#[test]
298-
fn test_double_nan() {
299-
let r = super::__adddf3(f64::NAN, 1.23);
300-
assert_eq!(r.repr(), f64::NAN.repr());
301-
}
302-
303-
#[test]
304-
fn test_float_inf() {
305-
let r = super::__addsf3(f32::INFINITY, -123.4);
306-
assert_eq!(r, f32::INFINITY);
307-
}
308-
309-
#[test]
310-
fn test_double_inf() {
311-
let r = super::__adddf3(f64::INFINITY, -123.4);
312-
assert_eq!(r, f64::INFINITY);
313-
}
314219
}

src/float/mod.rs

+57-18
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ pub mod add;
66
pub trait Float: Sized {
77
/// A uint of the same with as the float
88
type Int;
9-
9+
1010
/// Returns the bitwidth of the float type
1111
fn bits() -> u32;
1212

@@ -16,17 +16,14 @@ pub trait Float: Sized {
1616
/// Returns the bitwidth of the significand
1717
fn significand_bits() -> u32;
1818

19-
/// Returns `self` transmuted to `Self::Int`
20-
fn repr(self) -> Self::Int;
19+
/// Returns a mask for the sign bit of `self`
20+
fn sign_mask() -> Self::Int;
2121

22-
#[cfg(test)]
23-
/// 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
25-
/// compared.
26-
fn eq_repr(self, rhs: Self) -> bool;
22+
/// Returns a mask for the exponent portion of `self`
23+
fn exponent_mask() -> Self::Int;
2724

28-
/// Returns a `Self::Int` transmuted back to `Self`
29-
fn from_repr(a: Self::Int) -> Self;
25+
/// Returns a mask for the significand portion of `self`
26+
fn significand_mask() -> Self::Int;
3027

3128
/// Returns the sign bit of `self`
3229
fn sign(self) -> bool;
@@ -37,6 +34,21 @@ pub trait Float: Sized {
3734
/// Returns the significand portion of `self`
3835
fn significand(self) -> Self::Int;
3936

37+
/// Returns `self` transmuted to `Self::Int`
38+
fn repr(self) -> Self::Int;
39+
40+
#[cfg(test)]
41+
/// Checks if two floats have the same bit representation. *Except* for NaNs! NaN can be
42+
/// represented in multiple different ways. This methods returns `true` if two NaNs are
43+
/// compared.
44+
fn eq_repr(self, rhs: Self) -> bool;
45+
46+
/// Returns a `Self::Int` transmuted back to `Self`
47+
fn from_repr(a: Self::Int) -> Self;
48+
49+
/// Constructs a `Self` from its parts
50+
fn from_parts(sign: bool, exponent: Self::Int, significand: Self::Int) -> Self;
51+
4052
/// Returns (normalized exponent, normalized significand)
4153
fn normalize(significand: Self::Int) -> (i32, Self::Int);
4254
}
@@ -52,6 +64,15 @@ impl Float for f32 {
5264
fn significand_bits() -> u32 {
5365
23
5466
}
67+
fn sign_mask() -> Self::Int {
68+
1 << (Self::bits() - 1)
69+
}
70+
fn exponent_mask() -> Self::Int {
71+
((1 << Self::exponent_bits()) - 1) << Self::significand_bits()
72+
}
73+
fn significand_mask() -> Self::Int {
74+
(1 << Self::significand_bits()) - 1
75+
}
5576
fn repr(self) -> Self::Int {
5677
unsafe { mem::transmute(self) }
5778
}
@@ -66,15 +87,20 @@ impl Float for f32 {
6687
fn from_repr(a: Self::Int) -> Self {
6788
unsafe { mem::transmute(a) }
6889
}
90+
91+
fn from_parts(sign: bool, exponent: Self::Int, significand: Self::Int) -> Self {
92+
Self::from_repr(((sign as Self::Int) << (Self::bits() - 1)) |
93+
exponent & Self::exponent_mask() |
94+
significand & Self::significand_mask())
95+
}
6996
fn sign(self) -> bool {
70-
(self.repr() & 1 << Self::bits()) != 0
97+
(self.repr() & Self::sign_mask()) != 0
7198
}
7299
fn exponent(self) -> Self::Int {
73-
self.repr() >> Self::significand_bits()
74-
& ((1 << Self::exponent_bits()) - 1)
100+
self.repr() >> Self::significand_bits() & Self::exponent_mask()
75101
}
76102
fn significand(self) -> Self::Int {
77-
self.repr() & ((1 << Self::significand_bits()) - 1)
103+
self.repr() & Self::significand_mask()
78104
}
79105
fn normalize(significand: Self::Int) -> (i32, Self::Int) {
80106
let shift = significand.leading_zeros()
@@ -93,6 +119,15 @@ impl Float for f64 {
93119
fn significand_bits() -> u32 {
94120
52
95121
}
122+
fn sign_mask() -> Self::Int {
123+
1 << (Self::bits() - 1)
124+
}
125+
fn exponent_mask() -> Self::Int {
126+
((1 << Self::exponent_bits()) - 1) << Self::significand_bits()
127+
}
128+
fn significand_mask() -> Self::Int {
129+
(1 << Self::significand_bits()) - 1
130+
}
96131
fn repr(self) -> Self::Int {
97132
unsafe { mem::transmute(self) }
98133
}
@@ -107,15 +142,19 @@ impl Float for f64 {
107142
fn from_repr(a: Self::Int) -> Self {
108143
unsafe { mem::transmute(a) }
109144
}
145+
fn from_parts(sign: bool, exponent: Self::Int, significand: Self::Int) -> Self {
146+
Self::from_repr(((sign as Self::Int) << (Self::bits() - 1)) |
147+
exponent & Self::exponent_mask() |
148+
significand & Self::significand_mask())
149+
}
110150
fn sign(self) -> bool {
111-
(self.repr() & 1 << Self::bits()) != 0
151+
(self.repr() & Self::sign_mask()) != 0
112152
}
113153
fn exponent(self) -> Self::Int {
114-
self.repr() >> Self::significand_bits()
115-
& ((1 << Self::exponent_bits()) - 1)
154+
self.repr() >> Self::significand_bits() & Self::exponent_mask()
116155
}
117156
fn significand(self) -> Self::Int {
118-
self.repr() & ((1 << Self::significand_bits()) - 1)
157+
self.repr() & Self::significand_mask()
119158
}
120159
fn normalize(significand: Self::Int) -> (i32, Self::Int) {
121160
let shift = significand.leading_zeros()

src/qc.rs

+52
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 {
@@ -142,3 +144,53 @@ macro_rules! arbitrary_large {
142144

143145
arbitrary_large!(I64: i64);
144146
arbitrary_large!(U64: u64);
147+
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, 1.0, $ty::NAN, $ty::INFINITY, -$ty::INFINITY
159+
];
160+
161+
if g.gen() { // Random special case
162+
let index: usize = g.gen();
163+
$TY(special[index % special.len()])
164+
} else if g.gen() { // Random anything
165+
let sign: bool = g.gen();
166+
let exponent: <$ty as Float>::Int = g.gen();
167+
let significand: <$ty as Float>::Int = g.gen();
168+
$TY($ty::from_parts(sign, exponent, significand))
169+
} else if g.gen() { // Denormalized
170+
let sign: bool = g.gen();
171+
let exponent: <$ty as Float>::Int = 0;
172+
let significand: <$ty as Float>::Int = g.gen();
173+
$TY($ty::from_parts(sign, exponent, significand))
174+
} else { // NaN variants
175+
let sign: bool = g.gen();
176+
let exponent: <$ty as Float>::Int = g.gen();
177+
let significand: <$ty as Float>::Int = 0;
178+
$TY($ty::from_parts(sign, exponent, significand))
179+
}
180+
}
181+
182+
fn shrink(&self) -> Box<Iterator<Item=$TY>> {
183+
::quickcheck::empty_shrinker()
184+
}
185+
}
186+
187+
impl fmt::Debug for $TY {
188+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
189+
fmt::Debug::fmt(&self.0, f)
190+
}
191+
}
192+
}
193+
}
194+
195+
arbitrary_float!(F32: f32);
196+
arbitrary_float!(F64: f64);

0 commit comments

Comments
 (0)