Skip to content

Commit cc51186

Browse files
committed
Add is_normal and classify methods to Float trait
1 parent a9ac2b9 commit cc51186

File tree

4 files changed

+170
-17
lines changed

4 files changed

+170
-17
lines changed

src/libcore/num/f32.rs

+54-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
//! Operations and constants for `f32`
1212
1313
use num::{Zero, One, strconv};
14+
use num::{FPCategory, FPNaN, FPInfinite , FPZero, FPSubnormal, FPNormal};
1415
use prelude::*;
1516

1617
pub use cmath::c_float_targ_consts::*;
@@ -568,12 +569,39 @@ impl Float for f32 {
568569
*self == Float::infinity() || *self == Float::neg_infinity()
569570
}
570571

571-
/// Returns `true` if the number is not infinite or NaN
572+
/// Returns `true` if the number is neither infinite or NaN
572573
#[inline(always)]
573574
fn is_finite(&self) -> bool {
574575
!(self.is_NaN() || self.is_infinite())
575576
}
576577

578+
/// Returns `true` if the number is neither zero, infinite, subnormal or NaN
579+
#[inline(always)]
580+
fn is_normal(&self) -> bool {
581+
match self.classify() {
582+
FPNormal => true,
583+
_ => false,
584+
}
585+
}
586+
587+
/// Returns the floating point category of the number. If only one property is going to
588+
/// be tested, it is generally faster to use the specific predicate instead.
589+
fn classify(&self) -> FPCategory {
590+
static EXP_MASK: u32 = 0x7f800000;
591+
static MAN_MASK: u32 = 0x007fffff;
592+
593+
match (
594+
unsafe { ::cast::transmute::<f32,u32>(*self) } & EXP_MASK,
595+
unsafe { ::cast::transmute::<f32,u32>(*self) } & MAN_MASK
596+
) {
597+
(EXP_MASK, 0) => FPInfinite,
598+
(EXP_MASK, _) => FPNaN,
599+
(exp, _) if exp != 0 => FPNormal,
600+
_ if self.is_zero() => FPZero,
601+
_ => FPSubnormal,
602+
}
603+
}
604+
577605
#[inline(always)]
578606
fn mantissa_digits() -> uint { 24 }
579607

@@ -846,6 +874,7 @@ impl num::FromStrRadix for f32 {
846874
#[cfg(test)]
847875
mod tests {
848876
use f32::*;
877+
use num::*;
849878
use super::*;
850879
use prelude::*;
851880

@@ -1041,4 +1070,28 @@ mod tests {
10411070
assert_eq!(Primitive::bits::<f32>(), sys::size_of::<f32>() * 8);
10421071
assert_eq!(Primitive::bytes::<f32>(), sys::size_of::<f32>());
10431072
}
1073+
1074+
#[test]
1075+
fn test_is_normal() {
1076+
assert!(!Float::NaN::<f32>().is_normal());
1077+
assert!(!Float::infinity::<f32>().is_normal());
1078+
assert!(!Float::neg_infinity::<f32>().is_normal());
1079+
assert!(!Zero::zero::<f32>().is_normal());
1080+
assert!(!Float::neg_zero::<f32>().is_normal());
1081+
assert!(1f32.is_normal());
1082+
assert!(1e-37f32.is_normal());
1083+
assert!(!1e-38f32.is_normal());
1084+
}
1085+
1086+
#[test]
1087+
fn test_classify() {
1088+
assert_eq!(Float::NaN::<f32>().classify(), FPNaN);
1089+
assert_eq!(Float::infinity::<f32>().classify(), FPInfinite);
1090+
assert_eq!(Float::neg_infinity::<f32>().classify(), FPInfinite);
1091+
assert_eq!(Zero::zero::<f32>().classify(), FPZero);
1092+
assert_eq!(Float::neg_zero::<f32>().classify(), FPZero);
1093+
assert_eq!(1f32.classify(), FPNormal);
1094+
assert_eq!(1e-37f32.classify(), FPNormal);
1095+
assert_eq!(1e-38f32.classify(), FPSubnormal);
1096+
}
10441097
}

src/libcore/num/f64.rs

+53-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
1313
use libc::c_int;
1414
use num::{Zero, One, strconv};
15+
use num::{FPCategory, FPNaN, FPInfinite , FPZero, FPSubnormal, FPNormal};
1516
use prelude::*;
1617

1718
pub use cmath::c_double_targ_consts::*;
@@ -611,12 +612,39 @@ impl Float for f64 {
611612
*self == Float::infinity() || *self == Float::neg_infinity()
612613
}
613614

614-
/// Returns `true` if the number is not infinite or NaN
615+
/// Returns `true` if the number is neither infinite or NaN
615616
#[inline(always)]
616617
fn is_finite(&self) -> bool {
617618
!(self.is_NaN() || self.is_infinite())
618619
}
619620

621+
/// Returns `true` if the number is neither zero, infinite, subnormal or NaN
622+
#[inline(always)]
623+
fn is_normal(&self) -> bool {
624+
match self.classify() {
625+
FPNormal => true,
626+
_ => false,
627+
}
628+
}
629+
630+
/// Returns the floating point category of the number. If only one property is going to
631+
/// be tested, it is generally faster to use the specific predicate instead.
632+
fn classify(&self) -> FPCategory {
633+
static EXP_MASK: u64 = 0x7ff0000000000000;
634+
static MAN_MASK: u64 = 0x000fffffffffffff;
635+
636+
match (
637+
unsafe { ::cast::transmute::<f64,u64>(*self) } & EXP_MASK,
638+
unsafe { ::cast::transmute::<f64,u64>(*self) } & MAN_MASK
639+
) {
640+
(EXP_MASK, 0) => FPInfinite,
641+
(EXP_MASK, _) => FPNaN,
642+
(exp, _) if exp != 0 => FPNormal,
643+
_ if self.is_zero() => FPZero,
644+
_ => FPSubnormal,
645+
}
646+
}
647+
620648
#[inline(always)]
621649
fn mantissa_digits() -> uint { 53 }
622650

@@ -889,6 +917,7 @@ impl num::FromStrRadix for f64 {
889917
#[cfg(test)]
890918
mod tests {
891919
use f64::*;
920+
use num::*;
892921
use super::*;
893922
use prelude::*;
894923

@@ -1088,4 +1117,27 @@ mod tests {
10881117
assert_eq!(Primitive::bits::<f64>(), sys::size_of::<f64>() * 8);
10891118
assert_eq!(Primitive::bytes::<f64>(), sys::size_of::<f64>());
10901119
}
1120+
1121+
#[test]
1122+
fn test_is_normal() {
1123+
assert!(!Float::NaN::<f64>().is_normal());
1124+
assert!(!Float::infinity::<f64>().is_normal());
1125+
assert!(!Float::neg_infinity::<f64>().is_normal());
1126+
assert!(!Zero::zero::<f64>().is_normal());
1127+
assert!(!Float::neg_zero::<f64>().is_normal());
1128+
assert!(1f64.is_normal());
1129+
assert!(1e-307f64.is_normal());
1130+
assert!(!1e-308f64.is_normal());
1131+
}
1132+
1133+
#[test]
1134+
fn test_classify() {
1135+
assert_eq!(Float::NaN::<f64>().classify(), FPNaN);
1136+
assert_eq!(Float::infinity::<f64>().classify(), FPInfinite);
1137+
assert_eq!(Float::neg_infinity::<f64>().classify(), FPInfinite);
1138+
assert_eq!(Zero::zero::<f64>().classify(), FPZero);
1139+
assert_eq!(Float::neg_zero::<f64>().classify(), FPZero);
1140+
assert_eq!(1e-307f64.classify(), FPNormal);
1141+
assert_eq!(1e-308f64.classify(), FPSubnormal);
1142+
}
10911143
}

src/libcore/num/float.rs

+44-15
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
use libc::c_int;
2424
use num::{Zero, One, strconv};
25+
use num::FPCategory;
2526
use prelude::*;
2627

2728
pub use f64::{add, sub, mul, div, rem, lt, le, eq, ne, ge, gt};
@@ -782,32 +783,37 @@ impl Primitive for float {
782783
783784
impl Float for float {
784785
#[inline(always)]
785-
fn NaN() -> float { 0.0 / 0.0 }
786+
fn NaN() -> float { Float::NaN::<f64>() as float }
786787
787788
#[inline(always)]
788-
fn infinity() -> float { 1.0 / 0.0 }
789+
fn infinity() -> float { Float::infinity::<f64>() as float }
789790
790791
#[inline(always)]
791-
fn neg_infinity() -> float { -1.0 / 0.0 }
792+
fn neg_infinity() -> float { Float::neg_infinity::<f64>() as float }
792793
793794
#[inline(always)]
794-
fn neg_zero() -> float { -0.0 }
795+
fn neg_zero() -> float { Float::neg_zero::<f64>() as float }
795796
796797
/// Returns `true` if the number is NaN
797798
#[inline(always)]
798-
fn is_NaN(&self) -> bool { *self != *self }
799+
fn is_NaN(&self) -> bool { (*self as f64).is_NaN() }
799800
800801
/// Returns `true` if the number is infinite
801802
#[inline(always)]
802-
fn is_infinite(&self) -> bool {
803-
*self == Float::infinity() || *self == Float::neg_infinity()
804-
}
803+
fn is_infinite(&self) -> bool { (*self as f64).is_infinite() }
805804
806-
/// Returns `true` if the number is not infinite or NaN
805+
/// Returns `true` if the number is neither infinite or NaN
807806
#[inline(always)]
808-
fn is_finite(&self) -> bool {
809-
!(self.is_NaN() || self.is_infinite())
810-
}
807+
fn is_finite(&self) -> bool { (*self as f64).is_finite() }
808+
809+
/// Returns `true` if the number is neither zero, infinite, subnormal or NaN
810+
#[inline(always)]
811+
fn is_normal(&self) -> bool { (*self as f64).is_normal() }
812+
813+
/// Returns the floating point category of the number. If only one property is going to
814+
/// be tested, it is generally faster to use the specific predicate instead.
815+
#[inline(always)]
816+
fn classify(&self) -> FPCategory { (*self as f64).classify() }
811817
812818
#[inline(always)]
813819
fn mantissa_digits() -> uint { Float::mantissa_digits::<f64>() }
@@ -844,9 +850,7 @@ impl Float for float {
844850
/// than if the operations were performed separately
845851
///
846852
#[inline(always)]
847-
fn ln_1p(&self) -> float {
848-
(*self as f64).ln_1p() as float
849-
}
853+
fn ln_1p(&self) -> float { (*self as f64).ln_1p() as float }
850854
851855
///
852856
/// Fused multiply-add. Computes `(self * a) + b` with only one rounding error. This
@@ -867,6 +871,7 @@ impl Float for float {
867871
868872
#[cfg(test)]
869873
mod tests {
874+
use num::*;
870875
use super::*;
871876
use prelude::*;
872877
@@ -1063,6 +1068,30 @@ mod tests {
10631068
assert_eq!(Primitive::bytes::<float>(), sys::size_of::<float>());
10641069
}
10651070
1071+
#[test]
1072+
fn test_is_normal() {
1073+
assert!(!Float::NaN::<float>().is_normal());
1074+
assert!(!Float::infinity::<float>().is_normal());
1075+
assert!(!Float::neg_infinity::<float>().is_normal());
1076+
assert!(!Zero::zero::<float>().is_normal());
1077+
assert!(!Float::neg_zero::<float>().is_normal());
1078+
assert!(1f.is_normal());
1079+
assert!(1e-307f.is_normal());
1080+
assert!(!1e-308f.is_normal());
1081+
}
1082+
1083+
#[test]
1084+
fn test_classify() {
1085+
assert_eq!(Float::NaN::<float>().classify(), FPNaN);
1086+
assert_eq!(Float::infinity::<float>().classify(), FPInfinite);
1087+
assert_eq!(Float::neg_infinity::<float>().classify(), FPInfinite);
1088+
assert_eq!(Zero::zero::<float>().classify(), FPZero);
1089+
assert_eq!(Float::neg_zero::<float>().classify(), FPZero);
1090+
assert_eq!(1f.classify(), FPNormal);
1091+
assert_eq!(1e-307f.classify(), FPNormal);
1092+
assert_eq!(1e-308f.classify(), FPSubnormal);
1093+
}
1094+
10661095
#[test]
10671096
pub fn test_to_str_exact_do_decimal() {
10681097
let s = to_str_exact(5.0, 4u);

src/libcore/num/num.rs

+19
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,23 @@ pub trait Int: Integer
237237
+ Bitwise
238238
+ BitCount {}
239239

240+
///
241+
/// Used for representing the classification of floating point numbers
242+
///
243+
#[deriving(Eq)]
244+
pub enum FPCategory {
245+
/// "Not a Number", often obtained by dividing by zero
246+
FPNaN,
247+
/// Positive or negative infinity
248+
FPInfinite ,
249+
/// Positive or negative zero
250+
FPZero,
251+
/// De-normalized floating point representation (less precise than `FPNormal`)
252+
FPSubnormal,
253+
/// A regular floating point number
254+
FPNormal,
255+
}
256+
240257
///
241258
/// Primitive floating point numbers
242259
///
@@ -253,6 +270,8 @@ pub trait Float: Real
253270
fn is_NaN(&self) -> bool;
254271
fn is_infinite(&self) -> bool;
255272
fn is_finite(&self) -> bool;
273+
fn is_normal(&self) -> bool;
274+
fn classify(&self) -> FPCategory;
256275

257276
fn mantissa_digits() -> uint;
258277
fn digits() -> uint;

0 commit comments

Comments
 (0)