@@ -90,15 +90,31 @@ impl<T: Component> IntoComponent<T> for T {
90
90
}
91
91
}
92
92
93
+ // C23 = 2^23, in f32
94
+ // C52 = 2^52, in f64
95
+ const C23 : u32 = 0x4b00_0000 ;
96
+ const C52 : u64 = 0x4330_0000_0000_0000 ;
97
+
98
+ // Float to uint conversion with rounding to nearest even number. Formula
99
+ // follows the form (x_f32 + C23_f32) - C23_u32, where x is the component. From
100
+ // Hacker's Delight, p. 378-380.
101
+ // Works on the range of [-0.25, 2^23] for f32, [-0.25, 2^52] for f64.
102
+ //
103
+ // Special cases:
104
+ // NaN -> uint::MAX
105
+ // inf -> uint::MAX
106
+ // -inf -> 0
107
+ // Greater than 2^23 for f64, 2^52 for f64 -> uint::MAX
93
108
macro_rules! convert_float_to_uint {
94
109
( $float: ident; direct ( $( $direct_target: ident) ,+) ; $( via $temporary: ident ( $( $target: ident) ,+) ; ) * ) => {
95
110
$(
96
111
impl IntoComponent <$direct_target> for $float {
97
112
#[ inline]
98
113
fn into_component( self ) -> $direct_target {
99
114
let max = $direct_target:: max_intensity( ) as $float;
100
- let scaled = self * max;
101
- clamp( scaled. round( ) , 0.0 , max) as $direct_target
115
+ let scaled = ( self * max) . min( max) ;
116
+ let f = scaled + f32 :: from_bits( C23 ) ;
117
+ ( f. to_bits( ) . saturating_sub( C23 ) ) as $direct_target
102
118
}
103
119
}
104
120
) +
@@ -109,15 +125,61 @@ macro_rules! convert_float_to_uint {
109
125
#[ inline]
110
126
fn into_component( self ) -> $target {
111
127
let max = $target:: max_intensity( ) as $temporary;
112
- let scaled = self as $temporary * max;
113
- clamp( scaled. round( ) , 0.0 , max) as $target
128
+ let scaled = ( self as $temporary * max) . min( max) ;
129
+ let f = scaled + f64 :: from_bits( C52 ) ;
130
+ ( f. to_bits( ) . saturating_sub( C52 ) ) as $target
114
131
}
115
132
}
116
133
) +
117
134
) *
118
135
} ;
119
136
}
120
137
138
+ // Double to uint conversion with rounding to nearest even number. Formula
139
+ // follows the form (x_f64 + C52_f64) - C52_u64, where x is the component.
140
+ macro_rules! convert_double_to_uint {
141
+ ( $double: ident; direct ( $( $direct_target: ident) ,+) ; ) => {
142
+ $(
143
+ impl IntoComponent <$direct_target> for $double {
144
+ #[ inline]
145
+ fn into_component( self ) -> $direct_target {
146
+ let max = $direct_target:: max_intensity( ) as $double;
147
+ let scaled = ( self * max) . min( max) ;
148
+ let f = scaled + f64 :: from_bits( C52 ) ;
149
+ ( f. to_bits( ) . saturating_sub( C52 ) ) as $direct_target
150
+ }
151
+ }
152
+ ) +
153
+ } ;
154
+ }
155
+
156
+ // Uint to float conversion with the formula (x_u32 + C23_u32) - C23_f32, where
157
+ // x is the component. We convert the component to f32 then multiply it by the
158
+ // reciprocal of the float representation max value for u8.
159
+ // Works on the range of [0, 2^23] for f32, [0, 2^52 - 1] for f64.
160
+ impl IntoComponent < f32 > for u8 {
161
+ #[ inline]
162
+ fn into_component ( self ) -> f32 {
163
+ let comp_u = self as u32 + C23 ;
164
+ let comp_f = f32:: from_bits ( comp_u) - f32:: from_bits ( C23 ) ;
165
+ let max_u = core:: u8:: MAX as u32 + C23 ;
166
+ let max_f = ( f32:: from_bits ( max_u) - f32:: from_bits ( C23 ) ) . recip ( ) ;
167
+ comp_f * max_f
168
+ }
169
+ }
170
+
171
+ // Uint to f64 conversion with the formula (x_u64 + C23_u64) - C23_f64.
172
+ impl IntoComponent < f64 > for u8 {
173
+ #[ inline]
174
+ fn into_component ( self ) -> f64 {
175
+ let comp_u = self as u64 + C52 ;
176
+ let comp_f = f64:: from_bits ( comp_u) - f64:: from_bits ( C52 ) ;
177
+ let max_u = core:: u8:: MAX as u64 + C52 ;
178
+ let max_f = ( f64:: from_bits ( max_u) - f64:: from_bits ( C52 ) ) . recip ( ) ;
179
+ comp_f * max_f
180
+ }
181
+ }
182
+
121
183
macro_rules! convert_uint_to_float {
122
184
( $uint: ident; $( via $temporary: ident ( $( $target: ident) ,+) ; ) * ) => {
123
185
$(
@@ -167,9 +229,8 @@ impl IntoComponent<f32> for f64 {
167
229
self as f32
168
230
}
169
231
}
170
- convert_float_to_uint ! ( f64 ; direct ( u8 , u16 , u32 , u64 , u128 ) ; ) ;
232
+ convert_double_to_uint ! ( f64 ; direct ( u8 , u16 , u32 , u64 , u128 ) ; ) ;
171
233
172
- convert_uint_to_float ! ( u8 ; via f32 ( f32 ) ; via f64 ( f64 ) ; ) ;
173
234
convert_uint_to_uint ! ( u8 ; via f32 ( u16 ) ; via f64 ( u32 , u64 , u128 ) ; ) ;
174
235
175
236
convert_uint_to_float ! ( u16 ; via f32 ( f32 ) ; via f64 ( f64 ) ; ) ;
@@ -183,3 +244,119 @@ convert_uint_to_uint!(u64; via f64 (u8, u16, u32, u128););
183
244
184
245
convert_uint_to_float ! ( u128 ; via f64 ( f32 , f64 ) ; ) ;
185
246
convert_uint_to_uint ! ( u128 ; via f64 ( u8 , u16 , u32 , u64 ) ; ) ;
247
+
248
+ #[ cfg( test) ]
249
+ mod test {
250
+ use crate :: IntoComponent ;
251
+ use approx:: assert_relative_eq;
252
+
253
+ #[ test]
254
+ fn float_to_uint ( ) {
255
+ let data = vec ! [
256
+ -800.0 ,
257
+ -0.3 ,
258
+ 0.0 ,
259
+ 0.005 ,
260
+ 0.024983 ,
261
+ 0.01 ,
262
+ 0.15 ,
263
+ 0.3 ,
264
+ 0.5 ,
265
+ 0.6 ,
266
+ 0.7 ,
267
+ 0.8 ,
268
+ 0.8444 ,
269
+ 0.9 ,
270
+ 0.955 ,
271
+ 0.999 ,
272
+ 1.0 ,
273
+ 1.4 ,
274
+ f32 :: from_bits( 0x4b44_0000 ) ,
275
+ std:: f32 :: MAX ,
276
+ std:: f32 :: MIN ,
277
+ std:: f32 :: NAN ,
278
+ std:: f32 :: INFINITY ,
279
+ std:: f32 :: NEG_INFINITY ,
280
+ ] ;
281
+
282
+ let expected = vec ! [
283
+ 0u8 , 0 , 0 , 1 , 6 , 3 , 38 , 76 , 128 , 153 , 178 , 204 , 215 , 230 , 244 , 255 , 255 , 255 , 255 , 255 ,
284
+ 0 , 255 , 255 , 0 ,
285
+ ] ;
286
+
287
+ for ( d, e) in data. into_iter ( ) . zip ( expected) {
288
+ assert_eq ! ( IntoComponent :: <u8 >:: into_component( d) , e) ;
289
+ }
290
+ }
291
+
292
+ #[ test]
293
+ fn double_to_uint ( ) {
294
+ let data = vec ! [
295
+ -800.0 ,
296
+ -0.3 ,
297
+ 0.0 ,
298
+ 0.005 ,
299
+ 0.024983 ,
300
+ 0.01 ,
301
+ 0.15 ,
302
+ 0.3 ,
303
+ 0.5 ,
304
+ 0.6 ,
305
+ 0.7 ,
306
+ 0.8 ,
307
+ 0.8444 ,
308
+ 0.9 ,
309
+ 0.955 ,
310
+ 0.999 ,
311
+ 1.0 ,
312
+ 1.4 ,
313
+ f64 :: from_bits( 0x4334_0000_0000_0000 ) ,
314
+ std:: f64 :: MAX ,
315
+ std:: f64 :: MIN ,
316
+ std:: f64 :: NAN ,
317
+ std:: f64 :: INFINITY ,
318
+ std:: f64 :: NEG_INFINITY ,
319
+ ] ;
320
+
321
+ let expected = vec ! [
322
+ 0u8 , 0 , 0 , 1 , 6 , 3 , 38 , 76 , 128 , 153 , 178 , 204 , 215 , 230 , 244 , 255 , 255 , 255 , 255 , 255 ,
323
+ 0 , 255 , 255 , 0 ,
324
+ ] ;
325
+
326
+ for ( d, e) in data. into_iter ( ) . zip ( expected) {
327
+ assert_eq ! ( IntoComponent :: <u8 >:: into_component( d) , e) ;
328
+ }
329
+ }
330
+
331
+ #[ test]
332
+ fn uint_to_float ( ) {
333
+ fn into_component_old ( n : u8 ) -> f32 {
334
+ let max = core:: u8:: MAX as f32 ;
335
+ let scaled = n as f32 / max;
336
+ scaled as f32
337
+ }
338
+
339
+ for n in ( 0 ..=255 ) . step_by ( 5 ) {
340
+ assert_relative_eq ! (
341
+ IntoComponent :: <f32 >:: into_component( n) ,
342
+ into_component_old( n)
343
+ )
344
+ }
345
+ }
346
+
347
+ #[ test]
348
+ fn uint_to_double ( ) {
349
+ fn into_component_old ( n : u8 ) -> f64 {
350
+ let max = core:: u8:: MAX as f64 ;
351
+ let scaled = n as f64 / max;
352
+ scaled as f64
353
+ }
354
+
355
+ for n in ( 0 ..=255 ) . step_by ( 5 ) {
356
+ assert_relative_eq ! (
357
+ IntoComponent :: <f64 >:: into_component( n) ,
358
+ into_component_old( n)
359
+ )
360
+ }
361
+ }
362
+ }
0 commit comments