use std::fmt; use rb_sys::{ rb_float_new_in_heap, rb_float_value, rb_flt_rationalize, rb_flt_rationalize_with_prec, rb_to_float, ruby_value_type, VALUE, }; #[cfg(ruby_use_flonum)] use crate::value::Flonum; use crate::{ error::{protect, Error}, into_value::IntoValue, numeric::Numeric, r_rational::RRational, try_convert::TryConvert, value::{ private::{self, ReprValue as _}, NonZeroValue, ReprValue, Value, }, Ruby, }; /// # `Float` /// /// Functions that can be used to create instances of [`Float`]. /// /// See also the [`Float`] type. impl Ruby { /// Create a new `Float` from an `f64`. /// /// # Examples /// /// ``` /// use magnus::{rb_assert, Error, Ruby}; /// /// fn example(ruby: &Ruby) -> Result<(), Error> { /// let f = ruby.float_from_f64(1.7272337110188893e-77); /// rb_assert!(ruby, "f == 1.7272337110188893e-77", f); /// /// let f = ruby.float_from_f64(1.7272337110188890e-77); /// rb_assert!(ruby, "f == 1.7272337110188890e-77", f); /// /// Ok(()) /// } /// # Ruby::init(example).unwrap() /// ``` #[inline] pub fn float_from_f64(&self, n: f64) -> Float { unsafe { #[cfg(ruby_use_flonum)] let val = Flonum::from_f64_impl(n) .map(|f| f.as_rb_value()) .unwrap_or_else(|| rb_float_new_in_heap(n)); #[cfg(not(ruby_use_flonum))] let val = rb_float_new_in_heap(n); Float::from_rb_value_unchecked(val) } } } /// A type wrapping either a [`Flonum`](crate::value::Flonum) or an /// [`RFloat`](crate::r_float::RFloat) value. /// /// See the [`ReprValue`] trait for additional methods available on this type. /// See [`Ruby`](Ruby#float) for methods to create a `Float`. #[derive(Clone, Copy)] #[repr(transparent)] pub struct Float(NonZeroValue); impl Float { /// Return `Some(Float)` if `val` is a `Float`, `None` otherwise. /// /// # Examples /// /// ``` /// use magnus::{eval, Float}; /// # let _cleanup = unsafe { magnus::embed::init() }; /// /// assert!(Float::from_value(eval("1.7272337110188893e-77").unwrap()).is_some()); /// assert!(Float::from_value(eval("1.7272337110188890e-77").unwrap()).is_some()); /// assert!(Float::from_value(eval("1").unwrap()).is_none()); /// ``` #[inline] pub fn from_value(val: Value) -> Option { unsafe { if cfg!(ruby_use_flonum) && val.is_flonum() { return Some(Self(NonZeroValue::new_unchecked(val))); } debug_assert_value!(val); (val.rb_type() == ruby_value_type::RUBY_T_FLOAT) .then(|| Self(NonZeroValue::new_unchecked(val))) } } #[inline] pub(crate) unsafe fn from_rb_value_unchecked(val: VALUE) -> Self { Self(NonZeroValue::new_unchecked(Value::new(val))) } /// Create a new `Float` from an `f64`. /// /// # Panics /// /// Panics if called from a non-Ruby thread. See [`Ruby::float_from_f64`] /// for the non-panicking version. /// /// # Examples /// /// ``` /// # #![allow(deprecated)] /// use magnus::{rb_assert, Float}; /// # let _cleanup = unsafe { magnus::embed::init() }; /// /// let f = Float::from_f64(1.7272337110188893e-77); /// rb_assert!("f == 1.7272337110188893e-77", f); /// /// let f = Float::from_f64(1.7272337110188890e-77); /// rb_assert!("f == 1.7272337110188890e-77", f); /// ``` #[cfg_attr( not(feature = "old-api"), deprecated(note = "please use `Ruby::float_from_f64` instead") )] #[cfg_attr(docsrs, doc(cfg(feature = "old-api")))] #[inline] pub fn from_f64(n: f64) -> Self { get_ruby!().float_from_f64(n) } /// Convert `self` to a `f64`. /// /// # Examples /// /// ``` /// use magnus::{eval, Float}; /// # let _cleanup = unsafe { magnus::embed::init() }; /// /// let f: Float = eval("2.0").unwrap(); /// assert_eq!(f.to_f64(), 2.0); /// ``` #[inline] pub fn to_f64(self) -> f64 { #[cfg(ruby_use_flonum)] if let Some(flonum) = Flonum::from_value(self.as_value()) { return flonum.to_f64(); } unsafe { rb_float_value(self.as_rb_value()) } } /// Returns a rational approximation of `self`. /// /// # Examples /// /// ``` /// use magnus::{rb_assert, Error, Ruby}; /// /// fn example(ruby: &Ruby) -> Result<(), Error> { /// let pi = ruby.float_from_f64(3.141592); /// /// let r = pi.rationalize_with_prec(ruby.float_from_f64(0.001)); /// rb_assert!(ruby, "r == 201/64r", r); /// /// let r = pi.rationalize_with_prec(ruby.float_from_f64(0.01)); /// rb_assert!(ruby, "r == 22/7r", r); /// /// let r = pi.rationalize_with_prec(ruby.float_from_f64(0.1)); /// rb_assert!(ruby, "r == 16/5r", r); /// /// let r = pi.rationalize_with_prec(ruby.float_from_f64(1.)); /// rb_assert!(ruby, "r == 3/1r", r); /// /// Ok(()) /// } /// # Ruby::init(example).unwrap() /// ``` pub fn rationalize_with_prec(self, prec: Self) -> RRational { unsafe { RRational::from_rb_value_unchecked(rb_flt_rationalize_with_prec( self.as_rb_value(), prec.as_rb_value(), )) } } /// Returns a rational approximation of `self`. /// /// # Examples /// /// ``` /// use magnus::{rb_assert, Error, Ruby}; /// /// fn example(ruby: &Ruby) -> Result<(), Error> { /// let pi = ruby.float_from_f64(3.141592); /// rb_assert!(ruby, "r = 392699/125000r", r = pi.rationalize()); /// /// Ok(()) /// } /// # Ruby::init(example).unwrap() /// ``` pub fn rationalize(self) -> RRational { unsafe { RRational::from_rb_value_unchecked(rb_flt_rationalize(self.as_rb_value())) } } } impl fmt::Display for Float { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", unsafe { self.to_s_infallible() }) } } impl fmt::Debug for Float { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.inspect()) } } impl IntoValue for Float { #[inline] fn into_value_with(self, _: &Ruby) -> Value { self.0.get() } } unsafe impl private::ReprValue for Float {} impl Numeric for Float {} impl ReprValue for Float {} impl TryConvert for Float { fn try_convert(val: Value) -> Result { match Self::from_value(val) { Some(i) => Ok(i), None => protect(|| { debug_assert_value!(val); unsafe { Self::from_rb_value_unchecked(rb_to_float(val.as_rb_value())) } }), } } }