artichoke_backend/convert/
float.rs1use crate::Artichoke;
2use crate::convert::UnboxRubyError;
3use crate::core::{ConvertMut, TryConvert, Value as _};
4use crate::error::Error;
5use crate::sys;
6use crate::types::{Ruby, Rust};
7use crate::value::Value;
8
9impl ConvertMut<f64, Value> for Artichoke {
11 fn convert_mut(&mut self, value: f64) -> Value {
12 let float = unsafe { self.with_ffi_boundary(|mrb| sys::mrb_sys_float_value(mrb, value)) };
13 self.protect(Value::from(float.unwrap()))
14 }
15}
16
17impl TryConvert<Value, f64> for Artichoke {
18 type Error = Error;
19
20 fn try_convert(&self, value: Value) -> Result<f64, Self::Error> {
21 let Ruby::Float = value.ruby_type() else {
22 return Err(UnboxRubyError::new(&value, Rust::Float).into());
23 };
24 let value = value.inner();
25 Ok(unsafe { sys::mrb_sys_float_to_cdouble(value) })
29 }
30}
31
32#[cfg(test)]
33mod tests {
34 use crate::test::prelude::*;
35
36 #[test]
37 fn fail_convert() {
38 let mut interp = interpreter();
39 let value = interp.eval(b"Object.new").unwrap();
41 let result = value.try_convert_into::<f64>(&interp);
42 assert!(result.is_err());
43 }
44
45 #[test]
46 fn prop_convert_to_float() {
47 let mut interp = interpreter();
48 run_arbitrary::<f64>(|f| {
49 let value = interp.convert_mut(f);
50 assert_eq!(value.ruby_type(), Ruby::Float);
51 });
52 }
53
54 #[test]
55 fn prop_float_with_value() {
56 let mut interp = interpreter();
57 run_arbitrary::<f64>(|f| {
58 let value = interp.convert_mut(f);
59 let inner = value.inner();
60 let cdouble = unsafe { sys::mrb_sys_float_to_cdouble(inner) };
61 if f.is_nan() {
62 assert!(cdouble.is_nan());
63 } else if f.is_infinite() {
64 assert!(f.is_infinite() && cdouble.signum() == f.signum());
65 } else if cdouble >= f {
66 let difference = cdouble - f;
67 assert!(difference < f64::EPSILON);
68 } else if f >= cdouble {
69 let difference = f - cdouble;
70 assert!(difference < f64::EPSILON);
71 } else {
72 panic!("Unexpected branch in float_with_value");
73 }
74 });
75 }
76
77 #[test]
78 fn prop_roundtrip() {
79 let mut interp = interpreter();
80 run_arbitrary::<f64>(|f| {
81 let value = interp.convert_mut(f);
82 let roundtrip_value = value.try_convert_into::<f64>(&interp).unwrap();
83 if f.is_nan() {
84 assert!(roundtrip_value.is_nan());
85 } else if f.is_infinite() {
86 assert!(roundtrip_value.is_infinite() && roundtrip_value.signum() == f.signum());
87 } else if roundtrip_value >= f {
88 let difference = roundtrip_value - f;
89 assert!(difference < f64::EPSILON);
90 } else if f >= roundtrip_value {
91 let difference = f - roundtrip_value;
92 assert!(difference < f64::EPSILON);
93 } else {
94 panic!("Unexpected branch in roundtrip");
95 }
96 });
97 }
98
99 #[test]
100 fn prop_roundtrip_err() {
101 let interp = interpreter();
102 run_arbitrary::<bool>(|b| {
103 let value = interp.convert(b);
104 let result = value.try_convert_into::<f64>(&interp);
105 assert!(result.is_err());
106 });
107 }
108}