@@ -16,9 +16,7 @@ use std::io;
16
16
use std:: mem;
17
17
use std:: num;
18
18
use collections:: hashmap;
19
-
20
- // NB: this can probably be rewritten in terms of num::Num
21
- // to be less f64-specific.
19
+ use std:: vec_ng:: Vec ;
22
20
23
21
fn f64_cmp ( x : f64 , y : f64 ) -> Ordering {
24
22
// arbitrarily decide that NaNs are larger than everything.
@@ -167,38 +165,111 @@ impl Summary {
167
165
}
168
166
169
167
impl < ' a > Stats for & ' a [ f64 ] {
170
-
171
- // FIXME #11059 handle NaN, inf and overflow
172
168
fn sum ( self ) -> f64 {
173
- let mut partials : ~[ f64 ] = ~[ ] ;
174
169
170
+ fn samesign ( x : f64 , y : f64 ) -> bool {
171
+ ( x >= 0f64 ) == ( y > 0f64 )
172
+ }
173
+
174
+ fn twosum ( x : f64 , y : f64 ) -> ( f64 , f64 ) {
175
+ let hi = x + y;
176
+ let lo = y - ( hi - x) ;
177
+ ( hi, lo)
178
+ }
179
+
180
+ let mut partials = Vec :: new ( ) ;
181
+ partials. push ( 0.0 ) ;
182
+ let twopow: f64 = num:: powf ( 2.0 , ( Float :: max_exp ( None :: < f64 > ) - 1 ) as f64 ) ;
175
183
for & mut x in self . iter ( ) {
176
- let mut j = 0 ;
177
- // This inner loop applies `hi`/`lo` summation to each
178
- // partial so that the list of partial sums remains exact.
179
- for i in range ( 0 , partials. len ( ) ) {
180
- let mut y = partials[ i] ;
181
- if num:: abs ( x) < num:: abs ( y) {
182
- mem:: swap ( & mut x, & mut y) ;
184
+ if x. is_nan ( ) {
185
+ return x;
186
+ } else if x. is_infinite ( ) {
187
+ * partials. get_mut ( 0 ) += x;
188
+ } else {
189
+ let mut i = 1 ;
190
+ for z in range ( 1 , partials. len ( ) ) {
191
+ let mut y = * partials. get ( z) ;
192
+ if num:: abs ( x) < num:: abs ( y) {
193
+ mem:: swap ( & mut x, & mut y) ;
194
+ }
195
+ let ( mut hi, mut lo) = twosum ( x, y) ;
196
+ if hi. is_infinite ( ) {
197
+ let sign = if hi > 0.0 { -1.0 } else { 1.0 } ;
198
+ x = x - twopow * sign - twopow * sign;
199
+ * partials. get_mut ( 0 ) += sign;
200
+ if num:: abs ( x) < num:: abs ( y) {
201
+ mem:: swap ( & mut x, & mut y) ;
202
+ }
203
+ let ( h, l) = twosum ( x, y) ;
204
+ hi = h;
205
+ lo = l;
206
+ }
207
+ if lo != 0.0 {
208
+ * partials. get_mut ( i) = lo;
209
+ i += 1 ;
210
+ }
211
+ x = hi;
183
212
}
184
- // Rounded `x+y` is stored in `hi` with round-off stored in
185
- // `lo`. Together `hi+lo` are exactly equal to `x+y`.
186
- let hi = x + y;
187
- let lo = y - ( hi - x) ;
188
- if lo != 0f64 {
189
- partials[ j] = lo;
190
- j += 1 ;
213
+ if i >= partials. len ( ) {
214
+ partials. push ( x) ;
215
+ } else {
216
+ * partials. get_mut ( i) = x;
217
+ partials. truncate ( i + 1 ) ;
191
218
}
192
- x = hi;
193
219
}
194
- if j >= partials. len ( ) {
195
- partials. push ( x) ;
220
+ }
221
+ // special cases arising from infinities
222
+ if partials. get ( 0 ) . is_infinite ( ) {
223
+ return * partials. get ( 0 ) ;
224
+ } else if partials. get ( 0 ) . is_nan ( ) {
225
+ fail ! ( "infinities of both signs in summands" ) ;
226
+ }
227
+
228
+ let plen = partials. len ( ) ;
229
+ if num:: abs ( * partials. get ( 0 ) ) == 1.0 && plen > 1 &&
230
+ !samesign ( * partials. get ( plen - 1 ) , * partials. get ( 0 ) ) {
231
+ let ( hi, lo) = twosum ( * partials. get ( 0 ) * twopow, 0.5 * * partials. get ( plen - 1 ) ) ;
232
+ if ( 2.0 * hi) . is_infinite ( ) {
233
+ if ( hi + 2.0 * lo - hi) == ( 2.0 * lo) && plen > 2 &&
234
+ samesign ( lo, * partials. get ( plen - 2 ) ) {
235
+ return 2.0 * ( hi + 2.0 * lo) ;
236
+ }
196
237
} else {
197
- partials[ j] = x;
198
- partials. truncate ( j+1 ) ;
238
+ if lo != 0.0 {
239
+ partials. push ( 2.0 * lo) ;
240
+ }
241
+ partials. push ( 2.0 * hi) ;
242
+ * partials. get_mut ( 0 ) = 0.0 ;
199
243
}
200
244
}
201
- partials. iter ( ) . fold ( 0.0 , |p, q| p + * q)
245
+ // if there is no overflow, compute the sum of a list of
246
+ // nonoverlapping floats.
247
+ if * partials. get ( 0 ) == 0.0 {
248
+ let mut total_so_far = partials. pop ( ) . expect ( "Impossible empty vector in Stats.sum" ) ;
249
+ loop {
250
+ let item = match partials. pop ( ) {
251
+ None => break , //partials is empty, we're finished
252
+ Some ( x) => x
253
+ } ;
254
+ let ( total, lo) = twosum ( total_so_far, item) ;
255
+ total_so_far = total;
256
+ if lo != 0.0 {
257
+ partials. push ( lo) ;
258
+ break ;
259
+ }
260
+ }
261
+
262
+ let len = partials. len ( ) ;
263
+ if len >= 2 {
264
+ let ( a, b) = ( * partials. get ( len - 1 ) , * partials. get ( len-2 ) ) ;
265
+ if samesign ( a, b) && ( total_so_far + 2.0 * a - total_so_far) == ( 2.0 * a) {
266
+ total_so_far += 2.0 * a;
267
+ * partials. get_mut ( len - 1 ) = -a;
268
+ }
269
+ }
270
+ return total_so_far;
271
+ }
272
+ fail ! ( "overflow in stats::sum" ) ;
202
273
}
203
274
204
275
fn min ( self ) -> f64 {
@@ -442,6 +513,7 @@ mod tests {
442
513
use stats:: write_boxplot;
443
514
use std:: io;
444
515
use std:: str;
516
+ use std:: f64;
445
517
446
518
macro_rules! assert_approx_eq(
447
519
( $a: expr, $b: expr) => ( {
@@ -1022,6 +1094,30 @@ mod tests {
1022
1094
fn test_sum_f64_between_ints_that_sum_to_0 ( ) {
1023
1095
assert_eq ! ( [ 1e30 , 1.2 , -1e30 ] . sum( ) , 1.2 ) ;
1024
1096
}
1097
+ #[ test]
1098
+ fn test_sum_infs_and_nans_f64 ( ) {
1099
+ assert_eq ! ( [ f64 :: INFINITY ] . sum( ) , f64 :: INFINITY ) ;
1100
+ assert_eq ! ( [ f64 :: INFINITY , f64 :: INFINITY ] . sum( ) , f64 :: INFINITY ) ;
1101
+
1102
+ assert ! ( [ f64 :: NAN ] . sum( ) . is_nan( ) ) ;
1103
+ assert ! ( [ f64 :: INFINITY , f64 :: NEG_INFINITY , f64 :: NAN ] . sum( ) . is_nan( ) ) ;
1104
+ }
1105
+ #[ test]
1106
+ #[ should_fail]
1107
+ fn test_sum_inf_and_neg_inf_f64 ( ) {
1108
+ [ f64:: INFINITY , f64:: NEG_INFINITY ] . sum ( ) ;
1109
+ }
1110
+ #[ test]
1111
+ fn test_sum_max_value_f64 ( ) {
1112
+ let n = f64:: MAX_VALUE ;
1113
+ assert_eq ! ( [ n, -n, n] . sum( ) , n) ;
1114
+ }
1115
+ #[ test]
1116
+ #[ should_fail]
1117
+ fn test_sum_overflow_f64 ( ) {
1118
+ let n = f64:: MAX_VALUE ;
1119
+ [ n, -n, n, n / 10.0 ] . sum ( ) ;
1120
+ }
1025
1121
}
1026
1122
1027
1123
#[ cfg( test) ]
0 commit comments