Thanks to visit codestin.com
Credit goes to docs.rs

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}