@@ -77,9 +77,7 @@ macro_rules! to_unsigned_integer_converter {
77
77
Number :: NegInt ( v) => {
78
78
Err ( TryFromNumberErrorKind :: NegativeToUnsignedLossyConversion ( v) . into( ) )
79
79
}
80
- Number :: Float ( v) => {
81
- Err ( TryFromNumberErrorKind :: FloatToIntegerLossyConversion ( v) . into( ) )
82
- }
80
+ Number :: Float ( v) => attempt_lossless!( v, $typ) ,
83
81
}
84
82
}
85
83
}
@@ -102,9 +100,7 @@ macro_rules! to_signed_integer_converter {
102
100
match value {
103
101
Number :: PosInt ( v) => Ok ( Self :: try_from( v) ?) ,
104
102
Number :: NegInt ( v) => Ok ( Self :: try_from( v) ?) ,
105
- Number :: Float ( v) => {
106
- Err ( TryFromNumberErrorKind :: FloatToIntegerLossyConversion ( v) . into( ) )
107
- }
103
+ Number :: Float ( v) => attempt_lossless!( v, $typ) ,
108
104
}
109
105
}
110
106
}
@@ -115,6 +111,17 @@ macro_rules! to_signed_integer_converter {
115
111
} ;
116
112
}
117
113
114
+ macro_rules! attempt_lossless {
115
+ ( $value: expr, $typ: ty) => { {
116
+ let converted = $value as $typ;
117
+ if ( converted as f64 == $value) {
118
+ Ok ( converted)
119
+ } else {
120
+ Err ( TryFromNumberErrorKind :: FloatToIntegerLossyConversion ( $value) . into( ) )
121
+ }
122
+ } } ;
123
+ }
124
+
118
125
/// Converts to a `u64`. The conversion fails if it is lossy.
119
126
impl TryFrom < Number > for u64 {
120
127
type Error = TryFromNumberError ;
@@ -125,9 +132,7 @@ impl TryFrom<Number> for u64 {
125
132
Number :: NegInt ( v) => {
126
133
Err ( TryFromNumberErrorKind :: NegativeToUnsignedLossyConversion ( v) . into ( ) )
127
134
}
128
- Number :: Float ( v) => {
129
- Err ( TryFromNumberErrorKind :: FloatToIntegerLossyConversion ( v) . into ( ) )
130
- }
135
+ Number :: Float ( v) => attempt_lossless ! ( v, u64 ) ,
131
136
}
132
137
}
133
138
}
@@ -142,9 +147,7 @@ impl TryFrom<Number> for i64 {
142
147
match value {
143
148
Number :: PosInt ( v) => Ok ( Self :: try_from ( v) ?) ,
144
149
Number :: NegInt ( v) => Ok ( v) ,
145
- Number :: Float ( v) => {
146
- Err ( TryFromNumberErrorKind :: FloatToIntegerLossyConversion ( v) . into ( ) )
147
- }
150
+ Number :: Float ( v) => attempt_lossless ! ( v, i64 ) ,
148
151
}
149
152
}
150
153
}
@@ -236,6 +239,7 @@ mod test {
236
239
}
237
240
) ) ;
238
241
}
242
+ assert_eq!( $typ:: try_from( Number :: Float ( 25.0 ) ) . unwrap( ) , 25 ) ;
239
243
} ;
240
244
}
241
245
@@ -302,6 +306,13 @@ mod test {
302
306
}
303
307
) ) ;
304
308
}
309
+
310
+ let range = || ( $typ:: MIN ..=$typ:: MAX ) ;
311
+
312
+ for val in range( ) . take( 1024 ) . chain( range( ) . rev( ) . take( 1024 ) ) {
313
+ assert_eq!( val, $typ:: try_from( Number :: Float ( val as f64 ) ) . unwrap( ) ) ;
314
+ $typ:: try_from( Number :: Float ( ( val as f64 ) + 0.1 ) ) . expect_err( "not equivalent" ) ;
315
+ }
305
316
} ;
306
317
}
307
318
@@ -318,6 +329,19 @@ mod test {
318
329
}
319
330
) ) ;
320
331
}
332
+ let range = || ( i64:: MIN ..=i64:: MAX ) ;
333
+
334
+ for val in range ( ) . take ( 1024 ) . chain ( range ( ) . rev ( ) . take ( 1024 ) ) {
335
+ // if we can actually represent the value
336
+ if ( ( val as f64 ) as i64 ) == val {
337
+ assert_eq ! ( val, i64 :: try_from( Number :: Float ( val as f64 ) ) . unwrap( ) ) ;
338
+ }
339
+ let fval = val as f64 ;
340
+ // at the limits of the range, we don't have this precision
341
+ if ( fval + 0.1 ) . fract ( ) != 0.0 {
342
+ i64:: try_from ( Number :: Float ( ( val as f64 ) + 0.1 ) ) . expect_err ( "not equivalent" ) ;
343
+ }
344
+ }
321
345
}
322
346
323
347
#[ test]
@@ -333,6 +357,11 @@ mod test {
333
357
#[ test]
334
358
fn to_i8 ( ) {
335
359
to_signed_converter_tests ! ( i8 ) ;
360
+ i8:: try_from ( Number :: Float ( -3200000.0 ) ) . expect_err ( "overflow" ) ;
361
+ i8:: try_from ( Number :: Float ( 32.1 ) ) . expect_err ( "imprecise" ) ;
362
+ i8:: try_from ( Number :: Float ( i8:: MAX as f64 + 0.1 ) ) . expect_err ( "imprecise" ) ;
363
+ i8:: try_from ( Number :: Float ( f64:: NAN ) ) . expect_err ( "nan" ) ;
364
+ i8:: try_from ( Number :: Float ( f64:: INFINITY ) ) . expect_err ( "nan" ) ;
336
365
}
337
366
338
367
#[ test]
0 commit comments