fixed_sqrt/
lib.rs

1//! Fast square root for [fixed-point numbers](https://docs.rs/fixed) using
2//! [integer square root](https://docs.rs/integer-sqrt) algorithm.
3//!
4//! [Repository](https://gitlab.com/spearman/fixed-sqrt-rs)
5//!
6//! # Speed
7//!
8//! Generally more than 2x faster than iterative `.sqrt()` method.
9//!
10//! # Implementation
11//!
12//! **Even fractional bits**
13//!
14//! `FastSqrt` is implemented for all *unsigned* fixed-point types with an
15//! *even* number of bits.
16//!
17//! `FastSqrt` is implemented for all *signed* fixed-point types with an even
18//! number of fractional bits, *except* for the case of *zero integer* bits
19//! (i.e. fractional bits equal to the total bit size of the type). This is
20//! because the range for these types is *[-0.5, 0.5)*, and square roots of
21//! numbers in the range *[0.25, 0.5)* will be *>= 0.5*, outside of the range of
22//! representable values for that type.
23//!
24//! **Odd fractional bits**
25//!
26//! Computing the square root with an *odd* number of fractional bits requires
27//! one extra bit to shift before performing the square root.
28//!
29//! In the case of *signed* fixed-point numbers, since square root is defined
30//! only for positive input values, all signed fixed-point numbers (up to and
31//! including `FixedI128`) can compute the square root *in-place* utilizing the
32//! sign bit for the overflow.
33//!
34//! For *unsigned* fixed-point numbers with odd fractional bits, if an extra bit
35//! is needed (i.e. if the most significant bit is 1), this requires a scalar
36//! cast to the next larger unsigned primitive type before computing the square
37//! root. As a result, the square root trait is *not* implemented for
38//! `FixedU128` types with an odd number fractional bits since that would
39//! require 256-bit unsigned integers, or else the domain would have to be
40//! restricted to the lower half of u128 values.
41//!
42//! # Accuracy
43//!
44//! The `errors` example can be run to see exhaustive error stats for 8-bit and
45//! 16-bit fixed-point types. The worst-case absolute error is shown to occur at
46//! the largest values, where the percentage error is small, and the worst-case
47//! percentage error occurs at the smallest values where the absolute error is
48//! small.
49//!
50//! CSV files suitable for graphing with gnuplot can also be generated by
51//! running the `errors` example with the `-p` flag.
52//!
53//! # Panics
54//!
55//! - Panics if called on a negative signed number
56
57#![no_std]
58#![cfg_attr(test, feature(test))]
59
60// Used to make the tests build and run.
61#[cfg(test)]
62#[macro_use]
63extern crate std;
64
65#[allow(unused_macros)]
66macro_rules! show {
67  ($e:expr) => { println!("{}: {:?}", stringify!($e), $e); }
68}
69
70#[allow(unused_macros)]
71macro_rules! bits8 {
72  ($e:expr) => { println!("{}: {:08b}", stringify!($e), $e); }
73}
74
75#[allow(unused_macros)]
76macro_rules! bits64 {
77  ($e:expr) => { println!("{}: {:064b}", stringify!($e), $e); }
78}
79
80use fixed::{FixedI8, FixedI16, FixedI32, FixedI64, FixedU8, FixedU16, FixedU32,
81  FixedU64, FixedI128, FixedU128};
82use fixed::traits::Fixed;
83use fixed::types::extra::*;
84use integer_sqrt::IntegerSquareRoot;
85
86pub mod traits;
87
88use self::traits::*;
89
90/// Fast square root algorithm for fixed-point numbers
91pub trait FastSqrt : Fixed {
92  fn fast_sqrt (self) -> Self;
93}
94
95////////////////////////////////////////////////////////////////////////////////
96//  unsigned
97////////////////////////////////////////////////////////////////////////////////
98
99macro_rules! impl_sqrt_unsigned {
100  ($unsigned:ident, $leq:ident, $higher:ty) => {
101    impl <U> FastSqrt for $unsigned <U> where U : $leq {
102      fn fast_sqrt (self) -> Self {
103        if U::USIZE % 2 == 0 {
104          // even fractional bits
105          $unsigned::from_bits (
106            self.to_bits().integer_sqrt() <<
107              (<$unsigned <U> as Fixed>::Frac::USIZE/2)
108          )
109        } else {
110          // odd fractional bits
111          let bits = self.to_bits();
112          let sqrt = if
113            bits & (1 as <$unsigned <U> as Fixed>::Bits).rotate_right (1) > 0
114          {
115            // NOTE: we compute on the unsigned integer type of the larger size
116            let bits = bits as $higher << 1;
117            let sqrt = bits.integer_sqrt() << (<$unsigned <U>
118              as Fixed>::Frac::USIZE/2);
119            // square root should be within max value
120            debug_assert!(
121              sqrt <= <$unsigned <U> as Fixed>::Bits::MAX as $higher);
122            sqrt as <$unsigned <U> as Fixed>::Bits
123          } else {
124            let bits = bits << 1;
125            bits.integer_sqrt() << (<$unsigned <U> as Fixed>::Frac::USIZE/2)
126          };
127          $unsigned::from_bits (sqrt)
128        }
129      }
130    }
131  }
132}
133
134macro_rules! impl_sqrt_unsigned_u128 {
135  ($unsigned:ident, $leq:ident) => {
136    impl <U> FastSqrt for $unsigned <U> where U : $leq + IsEven {
137      fn fast_sqrt (self) -> Self {
138        $unsigned::from_bits (
139          self.to_bits().integer_sqrt() <<
140            (<$unsigned <U> as Fixed>::Frac::USIZE/2)
141        )
142      }
143    }
144  }
145}
146
147impl_sqrt_unsigned!(FixedU8,   LeEqU8,   u16);
148impl_sqrt_unsigned!(FixedU16,  LeEqU16,  u32);
149impl_sqrt_unsigned!(FixedU32,  LeEqU32,  u64);
150impl_sqrt_unsigned!(FixedU64,  LeEqU64,  u128);
151impl_sqrt_unsigned_u128!(FixedU128, LeEqU128);
152
153
154////////////////////////////////////////////////////////////////////////////////
155//  signed
156////////////////////////////////////////////////////////////////////////////////
157
158macro_rules! impl_sqrt_signed {
159  ($signed:ident, $lt:ident, $unsigned:ty) => {
160    impl <U> FastSqrt for $signed <U> where U : $lt {
161      fn fast_sqrt (self) -> Self {
162        if self.is_negative() {
163          panic!("fixed point square root of a negative number");
164        }
165        let bits = if U::USIZE % 2 == 0 {
166          // NOTE: as of integer-sqrt v0.1.2 there seems to be a bug when
167          // computing sqrt using signed 32bit and 128bit integers, we can just
168          // use unsigned integers instead
169          self.to_bits() as $unsigned
170        } else {
171          // because the msb of a non-negative number is zero, it is always safe
172          // to shift, but we need to compute the square root on the unsigned
173          // integer type
174          debug_assert_eq!(
175            self.to_bits() &
176              (1 as <$signed <U> as Fixed>::Bits).rotate_right (1),
177            0x0);
178          // NOTE: we compute on the unsigned integer type of the same size
179          // since the sign bit is zero we can shift into it
180          (self.to_bits() << 1) as $unsigned
181        };
182        let sqrt = bits.integer_sqrt() <<
183          (<$signed <U> as Fixed>::Frac::USIZE/2);
184        let n = $signed::from_bits (sqrt as <$signed <U> as Fixed>::Bits);
185        // NOTE: by excluding the case with zero integer bits, this assertion
186        // should never fail for non-zero even or odd fractional bits
187        debug_assert!(n.count_ones() == 0 || n.is_positive());
188        n
189      }
190    }
191  }
192}
193
194impl_sqrt_signed!(FixedI8,   LtU8,   u8);
195impl_sqrt_signed!(FixedI16,  LtU16,  u16);
196impl_sqrt_signed!(FixedI32,  LtU32,  u32);
197impl_sqrt_signed!(FixedI64,  LtU64,  u64);
198impl_sqrt_signed!(FixedI128, LtU128, u128);
199
200////////////////////////////////////////////////////////////////////////////////
201//  tests
202////////////////////////////////////////////////////////////////////////////////
203
204#[cfg(test)]
205mod tests {
206  extern crate test;
207  use super::*;
208  use fixed::types::*;
209  //use fixed::types::extra::*;
210  use typenum::{B0, B1, Shright, Sub1, UInt};
211
212  #[test]
213  fn test_sqrt() {
214    let x = I16F16::from_num (2);
215    assert_eq!(x.fast_sqrt(), I16F16::from_num (1.41406));
216
217    macro_rules! test_sqrt_unsigned {
218      ( $fun_zero:ident, $fun_even:ident, $(($fun_odd:ident),)? $fixed:ident,
219        $unsigned:ident, $maxerr:expr
220      ) => {
221        fn $fun_zero (base: f64, range: i32) {
222          for i in 0..range {
223            let h_f64 = base.powi(i);
224            let l_f64 = base.powi(-i);
225            if let Some (h) = $fixed::<U0>::checked_from_num(h_f64) {
226              let h_sqrt = h.fast_sqrt();
227              let err = (h_f64.sqrt() - h_sqrt.to_num::<f64>()).abs();
228              if err > $maxerr {
229                let ftype = format!("{}<U{}>",
230                  stringify!($fixed), <U0>::USIZE);
231                show!((ftype, h, h_sqrt, err));
232                assert!(err <= $maxerr);
233              }
234            }
235            if let Some (l) = $fixed::<U0>::checked_from_num(l_f64) {
236              let l_sqrt = l.fast_sqrt();
237              let err = (l_f64.sqrt() - l_sqrt.to_num::<f64>()).abs();
238              if err > $maxerr {
239                let ftype = format!("{}<U{}>",
240                  stringify!($fixed), <U0>::USIZE);
241                show!((ftype, l, l_sqrt, err));
242                assert!(err <= $maxerr);
243              }
244            }
245          }
246        }
247
248        fn $fun_even<U>(base: f64, range: i32) where
249          UInt <U, B0> : Unsigned + IsLessOrEqual<$unsigned, Output = True>
250        {
251          for i in 0..range {
252            let h_f64 = base.powi(i);
253            let l_f64 = base.powi(-i);
254            if let Some (h) = $fixed::<UInt <U, B0>>::checked_from_num(h_f64) {
255              let h_sqrt = h.fast_sqrt();
256              let err = (h_f64.sqrt() - h_sqrt.to_num::<f64>()).abs();
257              if err > $maxerr {
258                let ftype = format!("{}<U{}>",
259                  stringify!($fixed), <UInt <U, B0>>::USIZE);
260                show!((ftype, h, h_sqrt, err));
261                assert!(err <= $maxerr);
262              }
263            }
264            if let Some (l) = $fixed::<UInt <U, B0>>::checked_from_num(l_f64) {
265              let l_sqrt = l.fast_sqrt();
266              let err = (l_f64.sqrt() - l_sqrt.to_num::<f64>()).abs();
267              if err > $maxerr {
268                let ftype = format!("{}<U{}>",
269                  stringify!($fixed), <UInt <U, B0>>::USIZE);
270                show!((ftype, l, l_sqrt, err));
271                assert!(err <= $maxerr);
272              }
273            }
274          }
275        }
276        $(
277        fn $fun_odd<U>(base: f64, range: i32) where
278          UInt <U, B1> : Unsigned + IsLessOrEqual<$unsigned, Output = True>
279        {
280          for i in 0..range {
281            let h_f64 = base.powi(i);
282            let l_f64 = base.powi(-i);
283            if let Some (h) = $fixed::<UInt <U, B1>>::checked_from_num(h_f64) {
284              let h_sqrt = h.fast_sqrt();
285              let err = (h_f64.sqrt() - h_sqrt.to_num::<f64>()).abs();
286              if err > $maxerr {
287                let ftype = format!("{}<U{}>",
288                  stringify!($fixed), <UInt <U, B1>>::USIZE);
289                show!((ftype, h, h_sqrt, err));
290                assert!(err <= $maxerr);
291              }
292            }
293            if let Some (l) = $fixed::<UInt <U, B1>>::checked_from_num(l_f64) {
294              let l_sqrt = l.fast_sqrt();
295              let err = (l_f64.sqrt() - l_sqrt.to_num::<f64>()).abs();
296              if err > $maxerr {
297                let ftype = format!("{}<U{}>",
298                  stringify!($fixed), <UInt <U, B1>>::USIZE);
299                show!((ftype, l, l_sqrt, err));
300                assert!(err <= $maxerr);
301              }
302            }
303          }
304        }
305        )?
306        eprint!("testing {},", stringify!($fun_even));
307        $(
308        eprint!("{}", stringify!($fun_odd));
309        )?
310        eprintln!();
311
312        $fun_zero (0.5, $unsigned::I32);
313        $fun_zero (2.0, $unsigned::I32);
314        $fun_zero (2.5, $unsigned::I32/2);
315        $fun_zero (3.0, $unsigned::I32/2);
316        $fun_zero (5.0, $unsigned::I32/2);
317
318        $fun_even::<U0>(0.5, $unsigned::I32);
319        $fun_even::<U0>(2.0, $unsigned::I32);
320        $fun_even::<U0>(2.5, $unsigned::I32/2);
321        $fun_even::<U0>(3.0, $unsigned::I32/2);
322        $fun_even::<U0>(5.0, $unsigned::I32/2);
323
324        $(
325        $fun_odd::<U1>(0.5, $unsigned::I32);
326        $fun_odd::<U1>(2.0, $unsigned::I32);
327        $fun_odd::<U1>(2.5, $unsigned::I32/2);
328        $fun_odd::<U1>(3.0, $unsigned::I32/2);
329        $fun_odd::<U1>(5.0, $unsigned::I32/2);
330        )?
331
332        $fun_even::<U2>(0.5, $unsigned::I32);
333        $fun_even::<U2>(2.0, $unsigned::I32);
334        $fun_even::<U2>(2.5, $unsigned::I32/2);
335        $fun_even::<U2>(3.0, $unsigned::I32/2);
336        $fun_even::<U2>(5.0, $unsigned::I32/2);
337
338        $fun_even::<Shright<Sub1<Sub1<$unsigned>>,U1>>(0.5, $unsigned::I32);
339        $fun_even::<Shright<Sub1<Sub1<$unsigned>>,U1>>(2.0, $unsigned::I32);
340        $fun_even::<Shright<Sub1<Sub1<$unsigned>>,U1>>(2.5, $unsigned::I32/2);
341        $fun_even::<Shright<Sub1<Sub1<$unsigned>>,U1>>(3.0, $unsigned::I32/2);
342        $fun_even::<Shright<Sub1<Sub1<$unsigned>>,U1>>(5.0, $unsigned::I32/2);
343
344        $(
345        $fun_odd::<Shright<Sub1<$unsigned>,U1>>(0.5, $unsigned::I32);
346        $fun_odd::<Shright<Sub1<$unsigned>,U1>>(2.0, $unsigned::I32);
347        $fun_odd::<Shright<Sub1<$unsigned>,U1>>(2.5, $unsigned::I32/2);
348        $fun_odd::<Shright<Sub1<$unsigned>,U1>>(3.0, $unsigned::I32/2);
349        $fun_odd::<Shright<Sub1<$unsigned>,U1>>(5.0, $unsigned::I32/2);
350        )?
351
352        $fun_even::<Shright<$unsigned,U1>>(0.5, $unsigned::I32);
353        $fun_even::<Shright<$unsigned,U1>>(2.0, $unsigned::I32);
354        $fun_even::<Shright<$unsigned,U1>>(2.5, $unsigned::I32/2);
355        $fun_even::<Shright<$unsigned,U1>>(3.0, $unsigned::I32/2);
356        $fun_even::<Shright<$unsigned,U1>>(5.0, $unsigned::I32/2);
357      }
358    }
359
360    test_sqrt_unsigned!(test_sqrt_u128_zero, test_sqrt_u128_even, FixedU128,
361      U128, 8.0);
362    test_sqrt_unsigned!(test_sqrt_u64_zero, test_sqrt_u64_even,
363      (test_sqrt_u64_odd), FixedU64, U64, 1.0);
364    test_sqrt_unsigned!(test_sqrt_u32_zero, test_sqrt_u32_even,
365      (test_sqrt_u32_odd), FixedU32, U32, 1.0);
366    test_sqrt_unsigned!(test_sqrt_u16_zero, test_sqrt_u16_even,
367      (test_sqrt_u16_odd), FixedU16, U16, 1.0);
368    test_sqrt_unsigned!(test_sqrt_u8_zero, test_sqrt_u8_even,
369      (test_sqrt_u8_odd),  FixedU8,  U8,  1.0);
370
371    macro_rules! test_sqrt_signed {
372      ( $fun_zero:ident, $fun_even:ident, $fun_odd:ident, $fixed:ident,
373        $unsigned:ident, $maxerr:expr
374      ) => {
375        fn $fun_zero (base: f64, range: i32) {
376          for i in 0..range {
377            let h_f64 = base.powi(i);
378            let l_f64 = base.powi(-i);
379            if let Some (h) = $fixed::<U0>::checked_from_num(h_f64) {
380              let h_sqrt = h.fast_sqrt();
381              let err = (h_f64.sqrt() - h_sqrt.to_num::<f64>()).abs();
382              if err > $maxerr {
383                let ftype = format!("{}<U{}>", stringify!($fixed), <U0>::USIZE);
384                show!((ftype, h, h_sqrt, err));
385                assert!(err <= $maxerr);
386              }
387            }
388            if let Some (l) = $fixed::<U0>::checked_from_num(l_f64) {
389              let l_sqrt = l.fast_sqrt();
390              let err = (l_f64.sqrt() - l_sqrt.to_num::<f64>()).abs();
391              if err > $maxerr {
392                let ftype = format!("{}<U{}>", stringify!($fixed), <U0>::USIZE);
393                show!((ftype, l, l_sqrt, err));
394                assert!(err <= $maxerr);
395              }
396            }
397          }
398        }
399
400        fn $fun_even<U>(base: f64, range: i32) where
401          UInt <U, B0> : Unsigned + typenum::IsLess <$unsigned, Output = True> +
402            IsLessOrEqual <$unsigned, Output = True>
403        {
404          for i in 0..range {
405            let h_f64 = base.powi(i);
406            let l_f64 = base.powi(-i);
407            if let Some (h) = $fixed::<UInt <U, B0>>::checked_from_num(h_f64) {
408              // skip out of range
409              if $fixed::<UInt <U, B0>>::checked_from_num(h_f64.sqrt()) .is_none() {
410                continue
411              }
412              let h_sqrt = h.fast_sqrt();
413              let err = h_f64.sqrt() - h_sqrt.to_num::<f64>();
414              if err > $maxerr {
415                let ftype = format!("{}<U{}>",
416                  stringify!($fixed), <UInt <U, B0>>::USIZE);
417                show!((ftype, h, h_sqrt, err));
418                assert!(err <= $maxerr);
419              }
420            }
421            if let Some (l) = $fixed::<UInt <U, B0>>::checked_from_num(l_f64) {
422              // skip out of range
423              if $fixed::<UInt <U, B0>>::checked_from_num(l_f64.sqrt()) .is_none() {
424                continue
425              }
426              let l_sqrt = l.fast_sqrt();
427              let err = l_f64.sqrt() - l_sqrt.to_num::<f64>();
428              if err > $maxerr {
429                let ftype = format!("{}<U{}>",
430                  stringify!($fixed), <UInt <U, B0>>::USIZE);
431                show!((ftype, l, l_sqrt, err));
432                assert!(err <= $maxerr);
433              }
434            }
435          }
436        }
437
438        fn $fun_odd<U>(base: f64, range: i32) where
439          UInt <U, B1> : Unsigned + typenum::IsLess <$unsigned, Output = True> +
440            IsLessOrEqual<$unsigned, Output = True>
441        {
442          for i in 0..range {
443            let h_f64 = base.powi(i);
444            let l_f64 = base.powi(-i);
445            if let Some (h) = $fixed::<UInt <U, B1>>::checked_from_num(h_f64) {
446              // skip out of range
447              if $fixed::<UInt <U, B1>>::checked_from_num(h_f64.sqrt()) .is_none() {
448                continue
449              }
450              let h_sqrt = h.fast_sqrt();
451              let err = h_f64.sqrt() - h_sqrt.to_num::<f64>();
452              if err > $maxerr {
453                let ftype = format!("{}<U{}>",
454                  stringify!($fixed), <UInt <U, B1>>::USIZE);
455                show!((ftype, h, h_sqrt, err));
456                assert!(err <= $maxerr);
457              }
458            }
459            if let Some (l) = $fixed::<UInt <U, B1>>::checked_from_num(l_f64) {
460              // skip out of range
461              if $fixed::<UInt <U, B1>>::checked_from_num(l_f64.sqrt()) .is_none() {
462                continue
463              }
464              let l_sqrt = l.fast_sqrt();
465              let err = l_f64.sqrt() - l_sqrt.to_num::<f64>();
466              if err > $maxerr {
467                let ftype = format!("{}<U{}>",
468                  stringify!($fixed), <UInt <U, B1>>::USIZE);
469                show!((ftype, l, l_sqrt, err));
470                assert!(err <= $maxerr);
471              }
472            }
473          }
474        }
475
476        $fun_zero (0.5, $unsigned::I32);
477        $fun_zero (2.0, $unsigned::I32);
478        $fun_zero (2.5, $unsigned::I32/2);
479        $fun_zero (3.0, $unsigned::I32/2);
480        $fun_zero (5.0, $unsigned::I32/2);
481
482        $fun_even::<U0>(0.5, $unsigned::I32);
483        $fun_even::<U0>(2.0, $unsigned::I32);
484        $fun_even::<U0>(2.5, $unsigned::I32/2);
485        $fun_even::<U0>(3.0, $unsigned::I32/2);
486        $fun_even::<U0>(5.0, $unsigned::I32/2);
487
488        $fun_odd::<U1>(0.5, $unsigned::I32);
489        $fun_odd::<U1>(2.0, $unsigned::I32);
490        $fun_odd::<U1>(2.5, $unsigned::I32/2);
491        $fun_odd::<U1>(3.0, $unsigned::I32/2);
492        $fun_odd::<U1>(5.0, $unsigned::I32/2);
493
494        $fun_even::<U2>(0.5, $unsigned::I32);
495        $fun_even::<U2>(2.0, $unsigned::I32);
496        $fun_even::<U2>(2.5, $unsigned::I32/2);
497        $fun_even::<U2>(3.0, $unsigned::I32/2);
498        $fun_even::<U2>(5.0, $unsigned::I32/2);
499
500        $fun_even::<Shright<Sub1<Sub1<$unsigned>>,U1>>(0.5, $unsigned::I32);
501        $fun_even::<Shright<Sub1<Sub1<$unsigned>>,U1>>(2.0, $unsigned::I32);
502        $fun_even::<Shright<Sub1<Sub1<$unsigned>>,U1>>(2.5, $unsigned::I32/2);
503        $fun_even::<Shright<Sub1<Sub1<$unsigned>>,U1>>(3.0, $unsigned::I32/2);
504        $fun_even::<Shright<Sub1<Sub1<$unsigned>>,U1>>(5.0, $unsigned::I32/2);
505
506        $fun_odd::<Shright<Sub1<$unsigned>,U1>>(0.5, $unsigned::I32);
507        $fun_odd::<Shright<Sub1<$unsigned>,U1>>(2.0, $unsigned::I32);
508        $fun_odd::<Shright<Sub1<$unsigned>,U1>>(2.5, $unsigned::I32/2);
509        $fun_odd::<Shright<Sub1<$unsigned>,U1>>(3.0, $unsigned::I32/2);
510        $fun_odd::<Shright<Sub1<$unsigned>,U1>>(5.0, $unsigned::I32/2);
511      }
512    }
513
514    test_sqrt_signed!(test_sqrt_i128_zero, test_sqrt_i128_even,
515      test_sqrt_i128_odd, FixedI128, U128, 8.0);
516    test_sqrt_signed!(test_sqrt_i64_zero, test_sqrt_i64_even, test_sqrt_i64_odd,
517      FixedI64, U64, 1.0);
518    test_sqrt_signed!(test_sqrt_i32_zero, test_sqrt_i32_even, test_sqrt_i32_odd,
519      FixedI32, U32, 1.0);
520    test_sqrt_signed!(test_sqrt_i16_zero, test_sqrt_i16_even, test_sqrt_i16_odd,
521      FixedI16, U16, 1.0);
522    test_sqrt_signed!(test_sqrt_i8_zero, test_sqrt_i8_even,  test_sqrt_i8_odd,
523      FixedI8,  U8,  1.0);
524  }
525
526  #[test]
527  #[should_panic]
528  fn test_sqrt_negative() {
529    I16F16::from_num (-1.0).fast_sqrt();
530  }
531
532  #[test]
533  fn test_sqrt_max() {
534    // test some misc max values
535    // NOTE: integer-sqrt v0.1.2 has a bug where these would fail for i32 and
536    // i128 types, so we changed the implementation to use unsigned instead of
537    // signed types
538    I4F4::MAX.fast_sqrt();
539    I8F8::MAX.fast_sqrt();
540    I16F16::MAX.fast_sqrt();
541    I32F32::MAX.fast_sqrt();
542    I64F64::MAX.fast_sqrt();
543  }
544
545  #[test]
546  fn test_sqrt_unsigned_exhaustive() {
547    macro_rules! test_unsigned_exhaustive {
548      ($unsigned:ident, $maxerr:expr) => {
549        let mut i = $unsigned::from_bits (0);
550        loop {
551          let err = (i.to_num::<f64>().sqrt() - i.fast_sqrt().to_num::<f64>()).abs();
552          if err >= $maxerr {
553            show!((stringify!($unsigned), i, i.fast_sqrt(), err));
554            assert!(err < $maxerr);
555          }
556          if i == $unsigned::MAX {
557            break
558          }
559          i += $unsigned::from_bits (1);
560        }
561      }
562    }
563    test_unsigned_exhaustive!(U8F0, 1.0);
564    test_unsigned_exhaustive!(U7F1, 1.0);
565    test_unsigned_exhaustive!(U6F2, 1.0);
566    test_unsigned_exhaustive!(U5F3, 1.0);
567    test_unsigned_exhaustive!(U4F4, 1.0);
568    test_unsigned_exhaustive!(U3F5, 1.0);
569    test_unsigned_exhaustive!(U2F6, 1.0);
570    test_unsigned_exhaustive!(U1F7, 1.0);
571    test_unsigned_exhaustive!(U0F8, 1.0);
572
573    test_unsigned_exhaustive!(U16F0, 1.0);
574    test_unsigned_exhaustive!(U15F1, 1.0);
575    test_unsigned_exhaustive!(U14F2, 1.0);
576    test_unsigned_exhaustive!(U13F3, 1.0);
577    test_unsigned_exhaustive!(U12F4, 1.0);
578    test_unsigned_exhaustive!(U11F5, 1.0);
579    test_unsigned_exhaustive!(U10F6, 1.0);
580    test_unsigned_exhaustive!(U9F7,  1.0);
581    test_unsigned_exhaustive!(U8F8,  1.0);
582    test_unsigned_exhaustive!(U7F9,  1.0);
583    test_unsigned_exhaustive!(U6F10, 1.0);
584    test_unsigned_exhaustive!(U5F11, 1.0);
585    test_unsigned_exhaustive!(U4F12, 1.0);
586    test_unsigned_exhaustive!(U3F13, 1.0);
587    test_unsigned_exhaustive!(U2F14, 1.0);
588    test_unsigned_exhaustive!(U1F15, 1.0);
589    test_unsigned_exhaustive!(U0F16, 1.0);
590  }
591
592  #[test]
593  fn test_sqrt_signed_exhaustive() {
594    macro_rules! test_signed_exhaustive {
595      ($signed:ident, $maxerr:expr) => {
596        let mut i = $signed::from_bits (0);
597        loop {
598          let err = (i.to_num::<f64>().sqrt() - i.fast_sqrt().to_num::<f64>()).abs();
599          if err >= $maxerr {
600            show!((stringify!($signed), i, i.fast_sqrt(), err));
601            assert!(err < $maxerr);
602          }
603          if i == $signed::MAX {
604            break
605          }
606          i += $signed::from_bits (1);
607        }
608      }
609    }
610    test_signed_exhaustive!(I8F0, 1.0);
611    test_signed_exhaustive!(I7F1, 1.0);
612    test_signed_exhaustive!(I6F2, 1.0);
613    test_signed_exhaustive!(I5F3, 1.0);
614    test_signed_exhaustive!(I4F4, 1.0);
615    test_signed_exhaustive!(I3F5, 1.0);
616    test_signed_exhaustive!(I2F6, 1.0);
617    test_signed_exhaustive!(I1F7, 1.0);
618    //test_signed_exhaustive!(I0F8, 1.0);   // unimplemented
619
620    test_signed_exhaustive!(I16F0, 1.0);
621    test_signed_exhaustive!(I15F1, 1.0);
622    test_signed_exhaustive!(I14F2, 1.0);
623    test_signed_exhaustive!(I13F3, 1.0);
624    test_signed_exhaustive!(I12F4, 1.0);
625    test_signed_exhaustive!(I11F5, 1.0);
626    test_signed_exhaustive!(I10F6, 1.0);
627    test_signed_exhaustive!(I9F7,  1.0);
628    test_signed_exhaustive!(I8F8,  1.0);
629    test_signed_exhaustive!(I7F9,  1.0);
630    test_signed_exhaustive!(I6F10, 1.0);
631    test_signed_exhaustive!(I5F11, 1.0);
632    test_signed_exhaustive!(I4F12, 1.0);
633    test_signed_exhaustive!(I3F13, 1.0);
634    test_signed_exhaustive!(I2F14, 1.0);
635    test_signed_exhaustive!(I1F15, 1.0);
636    //test_signed_exhaustive!(I0F16, 1.0);  // unimplemented
637  }
638
639  #[bench]
640  fn bench_sqrt_fast (b : &mut test::Bencher) {
641    let x = I32F32::from_num (2);
642    let y = I32F32::from_num (5);
643    b.iter(||{
644      assert_eq!(x.fast_sqrt(), I32F32::from_num (1.414199829));
645      assert_eq!(y.fast_sqrt(), I32F32::from_num (2.2360534668));
646    });
647  }
648
649  #[bench]
650  fn bench_sqrt_fixed (b : &mut test::Bencher) {
651    let x = I32F32::from_num (2);
652    let y = I32F32::from_num (5);
653    b.iter(||{
654      assert_eq!(x.sqrt(), I32F32::from_num (1.4142135622));
655      assert_eq!(y.sqrt(), I32F32::from_num (2.2360679773));
656    });
657
658  }
659}