Skip to main content

reifydb_value/value/number/safe/convert/
f64.rs

1// SPDX-License-Identifier: MIT
2// Copyright (c) 2026 ReifyDB
3
4use super::*;
5
6impl_safe_convert_float_demote!(f64 => f32);
7
8impl_safe_convert_float_to_signed!(f64 => i8, i16, i32, i64, i128);
9
10impl_safe_convert_float_to_unsigned!(f64 => u8, u16, u32, u64, u128);
11
12impl_safe_convert_float_to_int!(f64);
13impl_safe_convert_float_to_uint!(f64);
14
15impl_safe_convert_to_decimal_from_float!(f64);
16
17#[cfg(test)]
18pub mod tests {
19
20	mod f32 {
21		use super::*;
22		use crate::value::number::safe::convert::SafeConvert;
23
24		#[test]
25		fn test_checked_convert_happy() {
26			let x: f64 = 123.0;
27			let y: Option<f32> = x.checked_convert();
28			assert_eq!(y, Some(123.0f32));
29		}
30
31		#[test]
32		fn test_checked_convert_unhappy_due_to_infinity() {
33			let x: f64 = f64::MAX;
34			let y: Option<f32> = x.checked_convert();
35			assert_eq!(y, None);
36		}
37
38		#[test]
39		fn test_checked_convert_unhappy_due_to_negative_infinity() {
40			let x: f64 = f64::MIN;
41			let y: Option<f32> = x.checked_convert();
42			assert_eq!(y, None);
43		}
44
45		#[test]
46		fn test_saturating_convert_within_range() {
47			let x: f64 = 456.789;
48			let y: f32 = x.saturating_convert();
49			assert_eq!(y, 456.789f32);
50		}
51
52		#[test]
53		fn test_saturating_convert_too_large() {
54			let x: f64 = f64::MAX;
55			let y: f32 = x.saturating_convert();
56			assert_eq!(y, f32::MAX);
57		}
58
59		#[test]
60		fn test_saturating_convert_too_small() {
61			let x: f64 = f64::MIN;
62			let y: f32 = x.saturating_convert();
63			assert_eq!(y, f32::MIN);
64		}
65
66		#[test]
67		fn test_saturating_convert_nan() {
68			let x: f64 = f64::NAN;
69			let y: f32 = x.saturating_convert();
70			assert!(y.is_nan());
71		}
72
73		#[test]
74		fn test_wrapping_convert_regular() {
75			let x: f64 = 789.123;
76			let y: f32 = x.wrapping_convert();
77			assert_eq!(y, 789.123f32);
78		}
79
80		#[test]
81		fn test_wrapping_convert_nan() {
82			let x: f64 = f64::NAN;
83			let y: f32 = x.wrapping_convert();
84			assert!(y.is_nan());
85		}
86
87		#[test]
88		fn test_wrapping_convert_infinity() {
89			let x: f64 = f64::INFINITY;
90			let y: f32 = x.wrapping_convert();
91			assert!(y.is_infinite() && y.is_sign_positive());
92		}
93	}
94
95	mod i8 {
96		use crate::value::number::safe::convert::SafeConvert;
97
98		#[test]
99		fn test_checked_convert_happy() {
100			let x: f64 = 42.0;
101			let y: Option<i8> = x.checked_convert();
102			assert_eq!(y, Some(42i8));
103		}
104
105		#[test]
106		fn test_checked_convert_unhappy() {
107			let x: f64 = 300.0;
108			let y: Option<i8> = x.checked_convert();
109			assert_eq!(y, None);
110		}
111
112		#[test]
113		fn test_checked_convert_negative() {
114			let x: f64 = -42.0;
115			let y: Option<i8> = x.checked_convert();
116			assert_eq!(y, Some(-42i8));
117		}
118
119		#[test]
120		fn test_checked_convert_nan() {
121			let x: f64 = f64::NAN;
122			let y: Option<i8> = x.checked_convert();
123			assert_eq!(y, None);
124		}
125
126		#[test]
127		fn test_checked_convert_infinity() {
128			let x: f64 = f64::INFINITY;
129			let y: Option<i8> = x.checked_convert();
130			assert_eq!(y, None);
131		}
132
133		#[test]
134		fn test_saturating_convert_overflow() {
135			let x: f64 = 300.0;
136			let y: i8 = x.saturating_convert();
137			assert_eq!(y, i8::MAX);
138		}
139
140		#[test]
141		fn test_saturating_convert_underflow() {
142			let x: f64 = -300.0;
143			let y: i8 = x.saturating_convert();
144			assert_eq!(y, i8::MIN);
145		}
146
147		#[test]
148		fn test_saturating_convert_nan() {
149			let x: f64 = f64::NAN;
150			let y: i8 = x.saturating_convert();
151			assert_eq!(y, 0);
152		}
153
154		#[test]
155		fn test_saturating_convert_infinity() {
156			let x: f64 = f64::INFINITY;
157			let y: i8 = x.saturating_convert();
158			assert_eq!(y, i8::MAX);
159		}
160
161		#[test]
162		fn test_saturating_convert_neg_infinity() {
163			let x: f64 = f64::NEG_INFINITY;
164			let y: i8 = x.saturating_convert();
165			assert_eq!(y, i8::MIN);
166		}
167
168		#[test]
169		fn test_wrapping_convert() {
170			let x: f64 = 42.0;
171			let y: i8 = x.wrapping_convert();
172			assert_eq!(y, 42i8);
173		}
174
175		#[test]
176		fn test_wrapping_convert_nan() {
177			let x: f64 = f64::NAN;
178			let y: i8 = x.wrapping_convert();
179			assert_eq!(y, 0);
180		}
181	}
182
183	mod i16 {
184		use crate::value::number::safe::convert::SafeConvert;
185
186		#[test]
187		fn test_checked_convert_happy() {
188			let x: f64 = 42.0;
189			let y: Option<i16> = x.checked_convert();
190			assert_eq!(y, Some(42i16));
191		}
192
193		#[test]
194		fn test_checked_convert_unhappy() {
195			let x: f64 = 40000.0;
196			let y: Option<i16> = x.checked_convert();
197			assert_eq!(y, None);
198		}
199
200		#[test]
201		fn test_checked_convert_negative() {
202			let x: f64 = -42.0;
203			let y: Option<i16> = x.checked_convert();
204			assert_eq!(y, Some(-42i16));
205		}
206
207		#[test]
208		fn test_checked_convert_nan() {
209			let x: f64 = f64::NAN;
210			let y: Option<i16> = x.checked_convert();
211			assert_eq!(y, None);
212		}
213
214		#[test]
215		fn test_saturating_convert_overflow() {
216			let x: f64 = 40000.0;
217			let y: i16 = x.saturating_convert();
218			assert_eq!(y, i16::MAX);
219		}
220
221		#[test]
222		fn test_saturating_convert_underflow() {
223			let x: f64 = -40000.0;
224			let y: i16 = x.saturating_convert();
225			assert_eq!(y, i16::MIN);
226		}
227
228		#[test]
229		fn test_wrapping_convert() {
230			let x: f64 = 42.0;
231			let y: i16 = x.wrapping_convert();
232			assert_eq!(y, 42i16);
233		}
234	}
235
236	mod i32 {
237		use crate::value::number::safe::convert::SafeConvert;
238
239		#[test]
240		fn test_checked_convert_happy() {
241			let x: f64 = 42.0;
242			let y: Option<i32> = x.checked_convert();
243			assert_eq!(y, Some(42i32));
244		}
245
246		#[test]
247		fn test_checked_convert_unhappy() {
248			let x: f64 = 3e38;
249			let y: Option<i32> = x.checked_convert();
250			assert_eq!(y, None);
251		}
252
253		#[test]
254		fn test_checked_convert_negative() {
255			let x: f64 = -42.0;
256			let y: Option<i32> = x.checked_convert();
257			assert_eq!(y, Some(-42i32));
258		}
259
260		#[test]
261		fn test_saturating_convert_overflow() {
262			let x: f64 = 3e38;
263			let y: i32 = x.saturating_convert();
264			assert_eq!(y, i32::MAX);
265		}
266
267		#[test]
268		fn test_saturating_convert_underflow() {
269			let x: f64 = -3e38;
270			let y: i32 = x.saturating_convert();
271			assert_eq!(y, i32::MIN);
272		}
273
274		#[test]
275		fn test_wrapping_convert() {
276			let x: f64 = 42.0;
277			let y: i32 = x.wrapping_convert();
278			assert_eq!(y, 42i32);
279		}
280	}
281
282	mod i64 {
283		use crate::value::number::safe::convert::SafeConvert;
284
285		#[test]
286		fn test_checked_convert_happy() {
287			let x: f64 = 42.0;
288			let y: Option<i64> = x.checked_convert();
289			assert_eq!(y, Some(42i64));
290		}
291
292		#[test]
293		fn test_checked_convert_unhappy() {
294			let x: f64 = 1e300;
295			let y: Option<i64> = x.checked_convert();
296			assert_eq!(y, None);
297		}
298
299		#[test]
300		fn test_checked_convert_negative() {
301			let x: f64 = -42.0;
302			let y: Option<i64> = x.checked_convert();
303			assert_eq!(y, Some(-42i64));
304		}
305
306		#[test]
307		fn test_saturating_convert_overflow() {
308			let x: f64 = 1e300;
309			let y: i64 = x.saturating_convert();
310			assert_eq!(y, i64::MAX);
311		}
312
313		#[test]
314		fn test_saturating_convert_underflow() {
315			let x: f64 = -1e300;
316			let y: i64 = x.saturating_convert();
317			assert_eq!(y, i64::MIN);
318		}
319
320		#[test]
321		fn test_wrapping_convert() {
322			let x: f64 = 42.0;
323			let y: i64 = x.wrapping_convert();
324			assert_eq!(y, 42i64);
325		}
326	}
327
328	mod i128 {
329		use crate::value::number::safe::convert::SafeConvert;
330
331		#[test]
332		fn test_checked_convert_happy() {
333			let x: f64 = 42.0;
334			let y: Option<i128> = x.checked_convert();
335			assert_eq!(y, Some(42i128));
336		}
337
338		#[test]
339		fn test_checked_convert_unhappy() {
340			let x: f64 = 1e300;
341			let y: Option<i128> = x.checked_convert();
342			assert_eq!(y, None);
343		}
344
345		#[test]
346		fn test_checked_convert_negative() {
347			let x: f64 = -42.0;
348			let y: Option<i128> = x.checked_convert();
349			assert_eq!(y, Some(-42i128));
350		}
351
352		#[test]
353		fn test_saturating_convert_overflow() {
354			let x: f64 = 1e300;
355			let y: i128 = x.saturating_convert();
356			assert_eq!(y, i128::MAX);
357		}
358
359		#[test]
360		fn test_saturating_convert_underflow() {
361			let x: f64 = -1e300;
362			let y: i128 = x.saturating_convert();
363			assert_eq!(y, i128::MIN);
364		}
365
366		#[test]
367		fn test_wrapping_convert() {
368			let x: f64 = 42.0;
369			let y: i128 = x.wrapping_convert();
370			assert_eq!(y, 42i128);
371		}
372	}
373
374	mod u8 {
375		use crate::value::number::safe::convert::SafeConvert;
376
377		#[test]
378		fn test_checked_convert_happy() {
379			let x: f64 = 42.0;
380			let y: Option<u8> = x.checked_convert();
381			assert_eq!(y, Some(42u8));
382		}
383
384		#[test]
385		fn test_checked_convert_unhappy() {
386			let x: f64 = 300.0;
387			let y: Option<u8> = x.checked_convert();
388			assert_eq!(y, None);
389		}
390
391		#[test]
392		fn test_checked_convert_negative() {
393			let x: f64 = -42.0;
394			let y: Option<u8> = x.checked_convert();
395			assert_eq!(y, None);
396		}
397
398		#[test]
399		fn test_checked_convert_nan() {
400			let x: f64 = f64::NAN;
401			let y: Option<u8> = x.checked_convert();
402			assert_eq!(y, None);
403		}
404
405		#[test]
406		fn test_checked_convert_infinity() {
407			let x: f64 = f64::INFINITY;
408			let y: Option<u8> = x.checked_convert();
409			assert_eq!(y, None);
410		}
411
412		#[test]
413		fn test_saturating_convert_overflow() {
414			let x: f64 = 300.0;
415			let y: u8 = x.saturating_convert();
416			assert_eq!(y, u8::MAX);
417		}
418
419		#[test]
420		fn test_saturating_convert_underflow() {
421			let x: f64 = -42.0;
422			let y: u8 = x.saturating_convert();
423			assert_eq!(y, 0);
424		}
425
426		#[test]
427		fn test_saturating_convert_nan() {
428			let x: f64 = f64::NAN;
429			let y: u8 = x.saturating_convert();
430			assert_eq!(y, 0);
431		}
432
433		#[test]
434		fn test_saturating_convert_infinity() {
435			let x: f64 = f64::INFINITY;
436			let y: u8 = x.saturating_convert();
437			assert_eq!(y, u8::MAX);
438		}
439
440		#[test]
441		fn test_wrapping_convert() {
442			let x: f64 = 42.0;
443			let y: u8 = x.wrapping_convert();
444			assert_eq!(y, 42u8);
445		}
446
447		#[test]
448		fn test_wrapping_convert_negative() {
449			let x: f64 = -42.0;
450			let y: u8 = x.wrapping_convert();
451			assert_eq!(y, 0);
452		}
453	}
454
455	mod u16 {
456		use crate::value::number::safe::convert::SafeConvert;
457
458		#[test]
459		fn test_checked_convert_happy() {
460			let x: f64 = 42.0;
461			let y: Option<u16> = x.checked_convert();
462			assert_eq!(y, Some(42u16));
463		}
464
465		#[test]
466		fn test_checked_convert_unhappy() {
467			let x: f64 = 70000.0;
468			let y: Option<u16> = x.checked_convert();
469			assert_eq!(y, None);
470		}
471
472		#[test]
473		fn test_checked_convert_negative() {
474			let x: f64 = -42.0;
475			let y: Option<u16> = x.checked_convert();
476			assert_eq!(y, None);
477		}
478
479		#[test]
480		fn test_saturating_convert_overflow() {
481			let x: f64 = 70000.0;
482			let y: u16 = x.saturating_convert();
483			assert_eq!(y, u16::MAX);
484		}
485
486		#[test]
487		fn test_saturating_convert_underflow() {
488			let x: f64 = -42.0;
489			let y: u16 = x.saturating_convert();
490			assert_eq!(y, 0);
491		}
492
493		#[test]
494		fn test_wrapping_convert() {
495			let x: f64 = 42.0;
496			let y: u16 = x.wrapping_convert();
497			assert_eq!(y, 42u16);
498		}
499	}
500
501	mod u32 {
502		use crate::value::number::safe::convert::SafeConvert;
503
504		#[test]
505		fn test_checked_convert_happy() {
506			let x: f64 = 42.0;
507			let y: Option<u32> = x.checked_convert();
508			assert_eq!(y, Some(42u32));
509		}
510
511		#[test]
512		fn test_checked_convert_unhappy() {
513			let x: f64 = 1e300;
514			let y: Option<u32> = x.checked_convert();
515			assert_eq!(y, None);
516		}
517
518		#[test]
519		fn test_checked_convert_negative() {
520			let x: f64 = -42.0;
521			let y: Option<u32> = x.checked_convert();
522			assert_eq!(y, None);
523		}
524
525		#[test]
526		fn test_saturating_convert_overflow() {
527			let x: f64 = 1e300;
528			let y: u32 = x.saturating_convert();
529			assert_eq!(y, u32::MAX);
530		}
531
532		#[test]
533		fn test_saturating_convert_underflow() {
534			let x: f64 = -42.0;
535			let y: u32 = x.saturating_convert();
536			assert_eq!(y, 0);
537		}
538
539		#[test]
540		fn test_wrapping_convert() {
541			let x: f64 = 42.0;
542			let y: u32 = x.wrapping_convert();
543			assert_eq!(y, 42u32);
544		}
545	}
546
547	mod u64 {
548		use crate::value::number::safe::convert::SafeConvert;
549
550		#[test]
551		fn test_checked_convert_happy() {
552			let x: f64 = 42.0;
553			let y: Option<u64> = x.checked_convert();
554			assert_eq!(y, Some(42u64));
555		}
556
557		#[test]
558		fn test_checked_convert_unhappy() {
559			let x: f64 = 1e300;
560			let y: Option<u64> = x.checked_convert();
561			assert_eq!(y, None);
562		}
563
564		#[test]
565		fn test_checked_convert_negative() {
566			let x: f64 = -42.0;
567			let y: Option<u64> = x.checked_convert();
568			assert_eq!(y, None);
569		}
570
571		#[test]
572		fn test_saturating_convert_overflow() {
573			let x: f64 = 1e300;
574			let y: u64 = x.saturating_convert();
575			assert_eq!(y, u64::MAX);
576		}
577
578		#[test]
579		fn test_saturating_convert_underflow() {
580			let x: f64 = -42.0;
581			let y: u64 = x.saturating_convert();
582			assert_eq!(y, 0);
583		}
584
585		#[test]
586		fn test_wrapping_convert() {
587			let x: f64 = 42.0;
588			let y: u64 = x.wrapping_convert();
589			assert_eq!(y, 42u64);
590		}
591	}
592
593	mod u128 {
594		use crate::value::number::safe::convert::SafeConvert;
595
596		#[test]
597		fn test_checked_convert_happy() {
598			let x: f64 = 42.0;
599			let y: Option<u128> = x.checked_convert();
600			assert_eq!(y, Some(42u128));
601		}
602
603		#[test]
604		fn test_checked_convert_unhappy() {
605			let x: f64 = 1e300;
606			let y: Option<u128> = x.checked_convert();
607			assert_eq!(y, None);
608		}
609
610		#[test]
611		fn test_checked_convert_negative() {
612			let x: f64 = -42.0;
613			let y: Option<u128> = x.checked_convert();
614			assert_eq!(y, None);
615		}
616
617		#[test]
618		fn test_saturating_convert_overflow() {
619			let x: f64 = 1e300;
620			let y: u128 = x.saturating_convert();
621			assert_eq!(y, u128::MAX);
622		}
623
624		#[test]
625		fn test_saturating_convert_underflow() {
626			let x: f64 = -42.0;
627			let y: u128 = x.saturating_convert();
628			assert_eq!(y, 0);
629		}
630
631		#[test]
632		fn test_wrapping_convert() {
633			let x: f64 = 42.0;
634			let y: u128 = x.wrapping_convert();
635			assert_eq!(y, 42u128);
636		}
637	}
638
639	mod decimal {
640		use crate::value::{decimal::Decimal, number::safe::convert::SafeConvert};
641
642		#[test]
643		fn test_checked_convert() {
644			let x: f64 = 42.5;
645			let y: Option<Decimal> = x.checked_convert();
646			assert!(y.is_some());
647			let decimal = y.unwrap();
648			assert_eq!(decimal.to_string(), "42.5");
649		}
650
651		#[test]
652		fn test_checked_convert_high_precision() {
653			let x: f64 = 123.456789;
654			let y: Option<Decimal> = x.checked_convert();
655			assert!(y.is_some());
656			let decimal = y.unwrap();
657			// f64 may add precision artifacts
658			assert!(decimal.to_string().starts_with("123.456789"));
659			// Precision and scale will be larger due to f64
660			// representation
661		}
662
663		#[test]
664		fn test_checked_convert_integer() {
665			let x: f64 = 1000.0;
666			let y: Option<Decimal> = x.checked_convert();
667			assert!(y.is_some());
668			let decimal = y.unwrap();
669			assert_eq!(decimal.to_string(), "1000");
670		}
671
672		#[test]
673		fn test_checked_convert_small_decimal() {
674			let x: f64 = 0.0000125;
675			let y: Option<Decimal> = x.checked_convert();
676			assert!(y.is_some());
677			let decimal = y.unwrap();
678			// f64 may have precision artifacts
679			assert!(decimal.to_string().starts_with("0.0000125"));
680			// Precision includes all digits including leading zeros
681			// after decimal
682		}
683
684		#[test]
685		fn test_checked_convert_negative() {
686			let x: f64 = -9876.543210;
687			let y: Option<Decimal> = x.checked_convert();
688			assert!(y.is_some());
689			let decimal = y.unwrap();
690			// f64 may have precision artifacts
691			assert!(decimal.to_string().starts_with("-9876.5432"));
692		}
693
694		#[test]
695		fn test_checked_convert_zero() {
696			let x: f64 = 0.0;
697			let y: Option<Decimal> = x.checked_convert();
698			assert!(y.is_some());
699			let decimal = y.unwrap();
700			assert_eq!(decimal.to_string(), "0");
701		}
702
703		#[test]
704		fn test_checked_convert_negative_zero() {
705			let x: f64 = -0.0;
706			let y: Option<Decimal> = x.checked_convert();
707			assert!(y.is_some());
708			let decimal = y.unwrap();
709			// -0.0 should convert to 0
710			assert_eq!(decimal.to_string(), "0");
711		}
712
713		#[test]
714		fn test_checked_convert_nan() {
715			let x: f64 = f64::NAN;
716			let y: Option<Decimal> = x.checked_convert();
717			assert!(y.is_none());
718		}
719
720		#[test]
721		fn test_checked_convert_infinity() {
722			let x: f64 = f64::INFINITY;
723			let y: Option<Decimal> = x.checked_convert();
724			assert!(y.is_none());
725		}
726
727		#[test]
728		fn test_checked_convert_neg_infinity() {
729			let x: f64 = f64::NEG_INFINITY;
730			let y: Option<Decimal> = x.checked_convert();
731			assert!(y.is_none());
732		}
733
734		#[test]
735		fn test_saturating_convert() {
736			let x: f64 = 999999.999999;
737			let y: Decimal = x.saturating_convert();
738			// f64 may have precision artifacts, check the integer
739			// part
740			let str_repr = y.to_string();
741			assert!(str_repr.starts_with("999999") || str_repr.starts_with("1000000"));
742			// Due to f64 rounding, value may be 1000000.0
743		}
744
745		#[test]
746		fn test_saturating_convert_nan() {
747			let x: f64 = f64::NAN;
748			let y: Decimal = x.saturating_convert();
749			assert_eq!(y.to_string(), "0");
750		}
751
752		#[test]
753		fn test_saturating_convert_infinity() {
754			let x: f64 = f64::INFINITY;
755			let y: Decimal = x.saturating_convert();
756			assert_eq!(y.to_string(), "0");
757		}
758
759		#[test]
760		fn test_wrapping_convert() {
761			let x: f64 = 42.0;
762			let y: Decimal = x.wrapping_convert();
763			assert_eq!(y.to_string(), "42");
764		}
765
766		#[test]
767		fn test_wrapping_convert_with_decimal() {
768			let x: f64 = 3.14159;
769			let y: Decimal = x.wrapping_convert();
770			// f64 may have precision artifacts
771			let str_repr = y.to_string();
772			// f64 representation of 3.14159 may not be exact
773			assert!(str_repr.starts_with("3.141"), "actual: {}", str_repr);
774		}
775	}
776}