1
- /* SPDX-License-Identifier: MIT
2
- * origin: musl src/math/ceil.c */
1
+ /* SPDX-License-Identifier: MIT */
2
+ / * origin: musl src/math/ceil.c */
3
3
4
- use super :: super :: { CastInto , Float } ;
4
+ //! Generic `ceil` algorithm.
5
+ //!
6
+ //! Note that this uses the algorithm from musl's `ceilf` rather than `ceil` or `ceill`, because
7
+ //! performance seems to be better (based on icount) and it does not seem to experience rounding
8
+ //! errors on i386.
9
+
10
+ use super :: super :: { Float , Int , IntTy , MinInt } ;
5
11
6
12
pub fn ceil < F : Float > ( x : F ) -> F {
7
- let toint = F :: ONE / F :: EPSILON ;
13
+ let zero = IntTy :: < F > :: ZERO ;
8
14
9
- // NB: using `exp` here and comparing to values adjusted by `EXP_BIAS` has better
10
- // perf than using `exp_unbiased` here.
11
- let e = x. exp ( ) ;
12
- let y: F ;
15
+ let mut ix = x. to_bits ( ) ;
16
+ let e = x. exp_unbiased ( ) ;
13
17
14
18
// If the represented value has no fractional part, no truncation is needed.
15
- if e >= ( F :: SIG_BITS + F :: EXP_BIAS ) . cast ( ) || x == F :: ZERO {
19
+ if e >= F :: SIG_BITS as i32 {
16
20
return x;
17
21
}
18
22
19
- let neg = x. is_sign_negative ( ) ;
23
+ if e >= 0 {
24
+ // |x| >= 1.0
25
+
26
+ let m = F :: SIG_MASK >> e. unsigned ( ) ;
27
+ if ( ix & m) == zero {
28
+ // Portion to be masked is already zero; no adjustment needed.
29
+ return x;
30
+ }
20
31
21
- // y = int(x) - x, where int(x) is an integer neighbor of x.
22
- // The `x - t + t - x` method is a way to expose non-round-to-even modes.
23
- y = if neg { x - toint + toint - x } else { x + toint - toint - x } ;
32
+ // Otherwise, raise an inexact exception.
33
+ force_eval ! ( x + F :: MAX ) ;
34
+ if x. is_sign_positive ( ) {
35
+ ix += m;
36
+ }
37
+ ix &= !m;
38
+ } else {
39
+ // |x| < 1.0, raise an inexact exception since truncation will happen (unless x == 0).
40
+ force_eval ! ( x + F :: MAX ) ;
24
41
25
- // Exp < 0; special case because of non-nearest rounding modes
26
- if e < F :: EXP_BIAS . cast ( ) {
27
- // Raise `FE_INEXACT`
28
- force_eval ! ( y) ;
29
- return if neg { F :: NEG_ZERO } else { F :: ONE } ;
42
+ if x. is_sign_negative ( ) {
43
+ // -1.0 < x <= -0.0; rounding up goes toward -0.0.
44
+ return F :: NEG_ZERO ;
45
+ } else if ix << 1 != zero {
46
+ // 0.0 < x < 1.0; rounding up goes toward +1.0.
47
+ return F :: ONE ;
48
+ }
30
49
}
31
50
32
- if y < F :: ZERO { x + y + F :: ONE } else { x + y }
51
+ F :: from_bits ( ix )
33
52
}
34
53
35
54
#[ cfg( test) ]
36
55
mod tests {
37
56
use super :: * ;
38
57
39
- #[ test]
40
- fn sanity_check_f64 ( ) {
41
- assert_eq ! ( ceil( 1.1f64 ) , 2.0 ) ;
42
- assert_eq ! ( ceil( 2.9f64 ) , 3.0 ) ;
43
- }
44
-
45
- /// The spec: https://en.cppreference.com/w/cpp/numeric/math/ceil
46
- #[ test]
47
- fn spec_tests_f64 ( ) {
58
+ /// Test against https://en.cppreference.com/w/cpp/numeric/math/ceil
59
+ fn spec_test < F : Float > ( ) {
48
60
// Not Asserted: that the current rounding mode has no effect.
49
- assert ! ( ceil( f64 :: NAN ) . is_nan( ) ) ;
50
- for f in [ 0.0 , -0.0 , f64:: INFINITY , f64:: NEG_INFINITY ] . iter ( ) . copied ( ) {
51
- assert_eq ! ( ceil( f) , f) ;
61
+ for f in [ F :: ZERO , F :: NEG_ZERO , F :: INFINITY , F :: NEG_INFINITY ] . iter ( ) . copied ( ) {
62
+ assert_biteq ! ( ceil( f) , f) ;
52
63
}
53
64
}
54
65
@@ -58,13 +69,19 @@ mod tests {
58
69
assert_eq ! ( ceil( 2.9f32 ) , 3.0 ) ;
59
70
}
60
71
61
- /// The spec: https://en.cppreference.com/w/cpp/numeric/math/ceil
62
72
#[ test]
63
73
fn spec_tests_f32 ( ) {
64
- // Not Asserted: that the current rounding mode has no effect.
65
- assert ! ( ceil( f32 :: NAN ) . is_nan( ) ) ;
66
- for f in [ 0.0 , -0.0 , f32:: INFINITY , f32:: NEG_INFINITY ] . iter ( ) . copied ( ) {
67
- assert_eq ! ( ceil( f) , f) ;
68
- }
74
+ spec_test :: < f32 > ( ) ;
75
+ }
76
+
77
+ #[ test]
78
+ fn sanity_check_f64 ( ) {
79
+ assert_eq ! ( ceil( 1.1f64 ) , 2.0 ) ;
80
+ assert_eq ! ( ceil( 2.9f64 ) , 3.0 ) ;
81
+ }
82
+
83
+ #[ test]
84
+ fn spec_tests_f64 ( ) {
85
+ spec_test :: < f64 > ( ) ;
69
86
}
70
87
}
0 commit comments