artichoke_backend/convert/
float_to_int.rs1use spinoso_exception::{FloatDomainError, RangeError};
2
3use crate::error::Error;
4
5#[expect(
18 clippy::cast_possible_truncation,
19 clippy::cast_precision_loss,
20 reason = "XXX: explain reason - https://github.com/artichoke/artichoke/pull/2813"
21)]
22pub fn float_to_int(float: f64) -> Result<i64, Error> {
23 if float.is_nan() {
24 return Err(FloatDomainError::with_message("NaN").into());
25 }
26 if float.is_sign_negative() {
27 if float.is_infinite() {
28 return Err(FloatDomainError::with_message("-Infinity").into());
29 }
30 let float = float.ceil();
39 if float < i64::MIN as f64 {
40 return Err(RangeError::with_message("too small for int").into());
41 }
42 Ok(float as i64)
43 } else {
44 if float.is_infinite() {
45 return Err(FloatDomainError::with_message("Infinity").into());
46 }
47 let float = float.floor();
56 if float > i64::MAX as f64 {
57 return Err(RangeError::with_message("too big for int").into());
58 }
59 Ok(float as i64)
60 }
61}
62
63#[cfg(test)]
64mod tests {
65 use bstr::ByteSlice;
66
67 use super::float_to_int;
68 use crate::test::prelude::*;
69
70 #[test]
71 fn float_to_int_rounds_to_zero() {
72 let result = float_to_int(10.0).unwrap();
73 assert_eq!(result, 10);
74 let result = float_to_int(10.2).unwrap();
75 assert_eq!(result, 10);
76 let result = float_to_int(10.5).unwrap();
77 assert_eq!(result, 10);
78 let result = float_to_int(10.9).unwrap();
79 assert_eq!(result, 10);
80
81 let result = float_to_int(-10.0).unwrap();
82 assert_eq!(result, -10);
83 let result = float_to_int(-10.2).unwrap();
84 assert_eq!(result, -10);
85 let result = float_to_int(-10.5).unwrap();
86 assert_eq!(result, -10);
87 let result = float_to_int(-10.9).unwrap();
88 assert_eq!(result, -10);
89 }
90
91 #[test]
92 fn float_nan_is_domain_error() {
93 let err = float_to_int(f64::NAN).unwrap_err();
94 assert_eq!(err.message().as_bstr(), b"NaN".as_bstr());
95 assert_eq!(err.name(), "FloatDomainError");
96 }
97
98 #[test]
99 fn float_infinities_are_domain_error() {
100 let err = float_to_int(f64::INFINITY).unwrap_err();
101 assert_eq!(err.message().as_bstr(), b"Infinity".as_bstr());
102 assert_eq!(err.name(), "FloatDomainError");
103
104 let err = float_to_int(f64::NEG_INFINITY).unwrap_err();
105 assert_eq!(err.message().as_bstr(), b"-Infinity".as_bstr());
106 assert_eq!(err.name(), "FloatDomainError");
107 }
108
109 #[test]
111 fn float_out_of_i64_range_is_range_error() {
112 let err = float_to_int(f64::MAX).unwrap_err();
113 assert_eq!(err.name(), "RangeError");
114
115 let err = float_to_int(f64::MIN).unwrap_err();
116 assert_eq!(err.name(), "RangeError");
117 }
118}