magnus/float.rs
1use std::fmt;
2
3use rb_sys::{
4 rb_float_new_in_heap, rb_float_value, rb_flt_rationalize, rb_flt_rationalize_with_prec,
5 rb_to_float, ruby_value_type, VALUE,
6};
7
8#[cfg(ruby_use_flonum)]
9use crate::value::Flonum;
10use crate::{
11 error::{protect, Error},
12 into_value::IntoValue,
13 numeric::Numeric,
14 r_rational::RRational,
15 try_convert::TryConvert,
16 value::{
17 private::{self, ReprValue as _},
18 NonZeroValue, ReprValue, Value,
19 },
20 Ruby,
21};
22
23/// # `Float`
24///
25/// Functions that can be used to create instances of [`Float`].
26///
27/// See also the [`Float`] type.
28impl Ruby {
29 /// Create a new `Float` from an `f64`.
30 ///
31 /// # Examples
32 ///
33 /// ```
34 /// use magnus::{rb_assert, Error, Ruby};
35 ///
36 /// fn example(ruby: &Ruby) -> Result<(), Error> {
37 /// let f = ruby.float_from_f64(1.7272337110188893e-77);
38 /// rb_assert!(ruby, "f == 1.7272337110188893e-77", f);
39 ///
40 /// let f = ruby.float_from_f64(1.7272337110188890e-77);
41 /// rb_assert!(ruby, "f == 1.7272337110188890e-77", f);
42 ///
43 /// Ok(())
44 /// }
45 /// # Ruby::init(example).unwrap()
46 /// ```
47 #[inline]
48 pub fn float_from_f64(&self, n: f64) -> Float {
49 unsafe {
50 #[cfg(ruby_use_flonum)]
51 let val = Flonum::from_f64_impl(n)
52 .map(|f| f.as_rb_value())
53 .unwrap_or_else(|| rb_float_new_in_heap(n));
54
55 #[cfg(not(ruby_use_flonum))]
56 let val = rb_float_new_in_heap(n);
57
58 Float::from_rb_value_unchecked(val)
59 }
60 }
61}
62
63/// A type wrapping either a [`Flonum`](crate::value::Flonum) or an
64/// [`RFloat`](crate::r_float::RFloat) value.
65///
66/// See the [`ReprValue`] trait for additional methods available on this type.
67/// See [`Ruby`](Ruby#float) for methods to create a `Float`.
68#[derive(Clone, Copy)]
69#[repr(transparent)]
70pub struct Float(NonZeroValue);
71
72impl Float {
73 /// Return `Some(Float)` if `val` is a `Float`, `None` otherwise.
74 ///
75 /// # Examples
76 ///
77 /// ```
78 /// use magnus::{eval, Float};
79 /// # let _cleanup = unsafe { magnus::embed::init() };
80 ///
81 /// assert!(Float::from_value(eval("1.7272337110188893e-77").unwrap()).is_some());
82 /// assert!(Float::from_value(eval("1.7272337110188890e-77").unwrap()).is_some());
83 /// assert!(Float::from_value(eval("1").unwrap()).is_none());
84 /// ```
85 #[inline]
86 pub fn from_value(val: Value) -> Option<Self> {
87 unsafe {
88 if cfg!(ruby_use_flonum) && val.is_flonum() {
89 return Some(Self(NonZeroValue::new_unchecked(val)));
90 }
91 debug_assert_value!(val);
92 (val.rb_type() == ruby_value_type::RUBY_T_FLOAT)
93 .then(|| Self(NonZeroValue::new_unchecked(val)))
94 }
95 }
96
97 #[inline]
98 pub(crate) unsafe fn from_rb_value_unchecked(val: VALUE) -> Self {
99 Self(NonZeroValue::new_unchecked(Value::new(val)))
100 }
101
102 /// Create a new `Float` from an `f64`.
103 ///
104 /// # Panics
105 ///
106 /// Panics if called from a non-Ruby thread. See [`Ruby::float_from_f64`]
107 /// for the non-panicking version.
108 ///
109 /// # Examples
110 ///
111 /// ```
112 /// # #![allow(deprecated)]
113 /// use magnus::{rb_assert, Float};
114 /// # let _cleanup = unsafe { magnus::embed::init() };
115 ///
116 /// let f = Float::from_f64(1.7272337110188893e-77);
117 /// rb_assert!("f == 1.7272337110188893e-77", f);
118 ///
119 /// let f = Float::from_f64(1.7272337110188890e-77);
120 /// rb_assert!("f == 1.7272337110188890e-77", f);
121 /// ```
122 #[cfg_attr(
123 not(feature = "old-api"),
124 deprecated(note = "please use `Ruby::float_from_f64` instead")
125 )]
126 #[cfg_attr(docsrs, doc(cfg(feature = "old-api")))]
127 #[inline]
128 pub fn from_f64(n: f64) -> Self {
129 get_ruby!().float_from_f64(n)
130 }
131
132 /// Convert `self` to a `f64`.
133 ///
134 /// # Examples
135 ///
136 /// ```
137 /// use magnus::{eval, Float};
138 /// # let _cleanup = unsafe { magnus::embed::init() };
139 ///
140 /// let f: Float = eval("2.0").unwrap();
141 /// assert_eq!(f.to_f64(), 2.0);
142 /// ```
143 #[inline]
144 pub fn to_f64(self) -> f64 {
145 #[cfg(ruby_use_flonum)]
146 if let Some(flonum) = Flonum::from_value(self.as_value()) {
147 return flonum.to_f64();
148 }
149 unsafe { rb_float_value(self.as_rb_value()) }
150 }
151
152 /// Returns a rational approximation of `self`.
153 ///
154 /// # Examples
155 ///
156 /// ```
157 /// use magnus::{rb_assert, Error, Ruby};
158 ///
159 /// fn example(ruby: &Ruby) -> Result<(), Error> {
160 /// let pi = ruby.float_from_f64(3.141592);
161 ///
162 /// let r = pi.rationalize_with_prec(ruby.float_from_f64(0.001));
163 /// rb_assert!(ruby, "r == 201/64r", r);
164 ///
165 /// let r = pi.rationalize_with_prec(ruby.float_from_f64(0.01));
166 /// rb_assert!(ruby, "r == 22/7r", r);
167 ///
168 /// let r = pi.rationalize_with_prec(ruby.float_from_f64(0.1));
169 /// rb_assert!(ruby, "r == 16/5r", r);
170 ///
171 /// let r = pi.rationalize_with_prec(ruby.float_from_f64(1.));
172 /// rb_assert!(ruby, "r == 3/1r", r);
173 ///
174 /// Ok(())
175 /// }
176 /// # Ruby::init(example).unwrap()
177 /// ```
178 pub fn rationalize_with_prec(self, prec: Self) -> RRational {
179 unsafe {
180 RRational::from_rb_value_unchecked(rb_flt_rationalize_with_prec(
181 self.as_rb_value(),
182 prec.as_rb_value(),
183 ))
184 }
185 }
186
187 /// Returns a rational approximation of `self`.
188 ///
189 /// # Examples
190 ///
191 /// ```
192 /// use magnus::{rb_assert, Error, Ruby};
193 ///
194 /// fn example(ruby: &Ruby) -> Result<(), Error> {
195 /// let pi = ruby.float_from_f64(3.141592);
196 /// rb_assert!(ruby, "r = 392699/125000r", r = pi.rationalize());
197 ///
198 /// Ok(())
199 /// }
200 /// # Ruby::init(example).unwrap()
201 /// ```
202 pub fn rationalize(self) -> RRational {
203 unsafe { RRational::from_rb_value_unchecked(rb_flt_rationalize(self.as_rb_value())) }
204 }
205}
206
207impl fmt::Display for Float {
208 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
209 write!(f, "{}", unsafe { self.to_s_infallible() })
210 }
211}
212
213impl fmt::Debug for Float {
214 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
215 write!(f, "{}", self.inspect())
216 }
217}
218
219impl IntoValue for Float {
220 #[inline]
221 fn into_value_with(self, _: &Ruby) -> Value {
222 self.0.get()
223 }
224}
225
226unsafe impl private::ReprValue for Float {}
227
228impl Numeric for Float {}
229
230impl ReprValue for Float {}
231
232impl TryConvert for Float {
233 fn try_convert(val: Value) -> Result<Self, Error> {
234 match Self::from_value(val) {
235 Some(i) => Ok(i),
236 None => protect(|| {
237 debug_assert_value!(val);
238 unsafe { Self::from_rb_value_unchecked(rb_to_float(val.as_rb_value())) }
239 }),
240 }
241 }
242}