77//! performance seems to be better (based on icount) and it does not seem to experience rounding
88//! errors on i386.
99
10+ use super :: super :: support:: { FpResult , Status } ;
1011use super :: super :: { Float , Int , IntTy , MinInt } ;
1112
1213pub fn ceil < F : Float > ( x : F ) -> F {
14+ ceil_status ( x) . val
15+ }
16+
17+ pub fn ceil_status < F : Float > ( x : F ) -> FpResult < F > {
1318 let zero = IntTy :: < F > :: ZERO ;
1419
1520 let mut ix = x. to_bits ( ) ;
1621 let e = x. exp_unbiased ( ) ;
1722
1823 // If the represented value has no fractional part, no truncation is needed.
1924 if e >= F :: SIG_BITS as i32 {
20- return x ;
25+ return FpResult :: ok ( x ) ;
2126 }
2227
23- if e >= 0 {
28+ let status;
29+ let res = if e >= 0 {
2430 // |x| >= 1.0
25-
2631 let m = F :: SIG_MASK >> e. unsigned ( ) ;
2732 if ( ix & m) == zero {
2833 // Portion to be masked is already zero; no adjustment needed.
29- return x ;
34+ return FpResult :: ok ( x ) ;
3035 }
3136
3237 // Otherwise, raise an inexact exception.
33- force_eval ! ( x + F :: MAX ) ;
38+ status = Status :: INEXACT ;
3439
3540 if x. is_sign_positive ( ) {
3641 ix += m;
@@ -40,7 +45,11 @@ pub fn ceil<F: Float>(x: F) -> F {
4045 F :: from_bits ( ix)
4146 } else {
4247 // |x| < 1.0, raise an inexact exception since truncation will happen (unless x == 0).
43- force_eval ! ( x + F :: MAX ) ;
48+ if ix & F :: SIG_MASK == F :: Int :: ZERO {
49+ status = Status :: OK ;
50+ } else {
51+ status = Status :: INEXACT ;
52+ }
4453
4554 if x. is_sign_negative ( ) {
4655 // -1.0 < x <= -0.0; rounding up goes toward -0.0.
@@ -52,18 +61,30 @@ pub fn ceil<F: Float>(x: F) -> F {
5261 // +0.0 remains unchanged
5362 x
5463 }
55- }
64+ } ;
65+
66+ FpResult :: new ( res, status)
5667}
5768
5869#[ cfg( test) ]
5970mod tests {
6071 use super :: * ;
72+ use crate :: support:: Hexf ;
6173
6274 /// Test against https://en.cppreference.com/w/cpp/numeric/math/ceil
63- fn spec_test < F : Float > ( ) {
64- // Not Asserted: that the current rounding mode has no effect.
65- for f in [ F :: ZERO , F :: NEG_ZERO , F :: INFINITY , F :: NEG_INFINITY ] . iter ( ) . copied ( ) {
66- assert_biteq ! ( ceil( f) , f) ;
75+ fn spec_test < F : Float > ( cases : & [ ( F , F , Status ) ] ) {
76+ let roundtrip = [ F :: ZERO , F :: ONE , F :: NEG_ONE , F :: NEG_ZERO , F :: INFINITY , F :: NEG_INFINITY ] ;
77+
78+ for x in roundtrip {
79+ let FpResult { val, status } = ceil_status ( x) ;
80+ assert_biteq ! ( val, x, "{}" , Hexf ( x) ) ;
81+ assert_eq ! ( status, Status :: OK , "{}" , Hexf ( x) ) ;
82+ }
83+
84+ for & ( x, res, res_stat) in cases {
85+ let FpResult { val, status } = ceil_status ( x) ;
86+ assert_biteq ! ( val, res, "{}" , Hexf ( x) ) ;
87+ assert_eq ! ( status, res_stat, "{}" , Hexf ( x) ) ;
6788 }
6889 }
6990
@@ -72,7 +93,17 @@ mod tests {
7293 #[ test]
7394 #[ cfg( f16_enabled) ]
7495 fn spec_tests_f16 ( ) {
75- spec_test :: < f16 > ( ) ;
96+ let cases = [
97+ ( 0.1 , 1.0 , Status :: INEXACT ) ,
98+ ( -0.1 , -0.0 , Status :: INEXACT ) ,
99+ ( 0.9 , 1.0 , Status :: INEXACT ) ,
100+ ( -0.9 , -0.0 , Status :: INEXACT ) ,
101+ ( 1.1 , 2.0 , Status :: INEXACT ) ,
102+ ( -1.1 , -1.0 , Status :: INEXACT ) ,
103+ ( 1.9 , 2.0 , Status :: INEXACT ) ,
104+ ( -1.9 , -1.0 , Status :: INEXACT ) ,
105+ ] ;
106+ spec_test :: < f16 > ( & cases) ;
76107 }
77108
78109 #[ test]
@@ -83,7 +114,17 @@ mod tests {
83114
84115 #[ test]
85116 fn spec_tests_f32 ( ) {
86- spec_test :: < f32 > ( ) ;
117+ let cases = [
118+ ( 0.1 , 1.0 , Status :: INEXACT ) ,
119+ ( -0.1 , -0.0 , Status :: INEXACT ) ,
120+ ( 0.9 , 1.0 , Status :: INEXACT ) ,
121+ ( -0.9 , -0.0 , Status :: INEXACT ) ,
122+ ( 1.1 , 2.0 , Status :: INEXACT ) ,
123+ ( -1.1 , -1.0 , Status :: INEXACT ) ,
124+ ( 1.9 , 2.0 , Status :: INEXACT ) ,
125+ ( -1.9 , -1.0 , Status :: INEXACT ) ,
126+ ] ;
127+ spec_test :: < f32 > ( & cases) ;
87128 }
88129
89130 #[ test]
@@ -94,12 +135,32 @@ mod tests {
94135
95136 #[ test]
96137 fn spec_tests_f64 ( ) {
97- spec_test :: < f64 > ( ) ;
138+ let cases = [
139+ ( 0.1 , 1.0 , Status :: INEXACT ) ,
140+ ( -0.1 , -0.0 , Status :: INEXACT ) ,
141+ ( 0.9 , 1.0 , Status :: INEXACT ) ,
142+ ( -0.9 , -0.0 , Status :: INEXACT ) ,
143+ ( 1.1 , 2.0 , Status :: INEXACT ) ,
144+ ( -1.1 , -1.0 , Status :: INEXACT ) ,
145+ ( 1.9 , 2.0 , Status :: INEXACT ) ,
146+ ( -1.9 , -1.0 , Status :: INEXACT ) ,
147+ ] ;
148+ spec_test :: < f64 > ( & cases) ;
98149 }
99150
100151 #[ test]
101152 #[ cfg( f128_enabled) ]
102153 fn spec_tests_f128 ( ) {
103- spec_test :: < f128 > ( ) ;
154+ let cases = [
155+ ( 0.1 , 1.0 , Status :: INEXACT ) ,
156+ ( -0.1 , -0.0 , Status :: INEXACT ) ,
157+ ( 0.9 , 1.0 , Status :: INEXACT ) ,
158+ ( -0.9 , -0.0 , Status :: INEXACT ) ,
159+ ( 1.1 , 2.0 , Status :: INEXACT ) ,
160+ ( -1.1 , -1.0 , Status :: INEXACT ) ,
161+ ( 1.9 , 2.0 , Status :: INEXACT ) ,
162+ ( -1.9 , -1.0 , Status :: INEXACT ) ,
163+ ] ;
164+ spec_test :: < f128 > ( & cases) ;
104165 }
105166}
0 commit comments