Skip to content

Commit

Permalink
float doc improvements
Browse files Browse the repository at this point in the history
+ fix inconsistency with RFloat::from_value
  • Loading branch information
matsadler committed Apr 26, 2023
1 parent dca7212 commit 3800efd
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 47 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
- `Error::runtime_error` (use `Error::new(exception::runtime_error(), msg)`).

### Fixed
- `RFloat::from_value` now returns `None` when value is a `Flonum`.

### Security

Expand Down
67 changes: 28 additions & 39 deletions src/float.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ 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_special_consts, ruby_value_type, VALUE,
rb_to_float, ruby_value_type, VALUE,
};

#[cfg(ruby_use_flonum)]
Expand Down Expand Up @@ -43,8 +43,8 @@ impl Ruby {
}
}

/// A type wrapping either a [`Flonum`](`crate::value::Flonum`) value or a
/// Value known to be an instance of Float.
/// 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`.
Expand All @@ -63,13 +63,12 @@ impl Float {
///
/// 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<Self> {
unsafe {
if val.as_rb_value() & ruby_special_consts::RUBY_FLONUM_MASK as VALUE
== ruby_special_consts::RUBY_FLONUM_FLAG as VALUE
{
if cfg!(ruby_use_flonum) && val.is_flonum() {
return Some(Self(NonZeroValue::new_unchecked(val)));
}
debug_assert_value!(val);
Expand All @@ -93,21 +92,14 @@ impl Float {
/// # Examples
///
/// ```
/// use magnus::{eval, Float};
/// use magnus::{rb_assert, Float};
/// # let _cleanup = unsafe { magnus::embed::init() };
///
/// let res: bool = eval!(
/// "f == 1.7272337110188893e-77",
/// f = Float::from_f64(1.7272337110188893e-77)
/// )
/// .unwrap();
/// assert!(res);
/// let res: bool = eval!(
/// "f == 1.7272337110188890e-77",
/// f = Float::from_f64(1.7272337110188890e-77)
/// )
/// .unwrap();
/// assert!(res);
/// 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(feature = "friendly-api")]
#[inline]
Expand All @@ -123,7 +115,8 @@ impl Float {
/// use magnus::{eval, Float};
/// # let _cleanup = unsafe { magnus::embed::init() };
///
/// assert_eq!(eval::<Float>("2.0").unwrap().to_f64(), 2.0);
/// let f: Float = eval("2.0").unwrap();
/// assert_eq!(f.to_f64(), 2.0);
/// ```
#[inline]
pub fn to_f64(self) -> f64 {
Expand All @@ -139,26 +132,22 @@ impl Float {
/// # Examples
///
/// ```
/// use magnus::Float;
/// use magnus::{rb_assert, Float};
/// # let _cleanup = unsafe { magnus::embed::init() };
///
/// let pi = Float::from_f64(3.141592);
/// assert_eq!(
/// pi.rationalize_with_prec(Float::from_f64(0.001)).to_string(),
/// "201/64"
/// );
/// assert_eq!(
/// pi.rationalize_with_prec(Float::from_f64(0.01)).to_string(),
/// "22/7"
/// );
/// assert_eq!(
/// pi.rationalize_with_prec(Float::from_f64(0.1)).to_string(),
/// "16/5"
/// );
/// assert_eq!(
/// pi.rationalize_with_prec(Float::from_f64(1.)).to_string(),
/// "3/1"
/// );
///
/// let r = pi.rationalize_with_prec(Float::from_f64(0.001));
/// rb_assert!("r == 201/64r", r);
///
/// let r = pi.rationalize_with_prec(Float::from_f64(0.01));
/// rb_assert!("r == 22/7r", r);
///
/// let r = pi.rationalize_with_prec(Float::from_f64(0.1));
/// rb_assert!("r == 16/5r", r);
///
/// let r = pi.rationalize_with_prec(Float::from_f64(1.));
/// rb_assert!("r == 3/1r", r);
/// ```
pub fn rationalize_with_prec(self, prec: Self) -> RRational {
unsafe {
Expand All @@ -174,11 +163,11 @@ impl Float {
/// # Examples
///
/// ```
/// use magnus::Float;
/// use magnus::{rb_assert, Float};
/// # let _cleanup = unsafe { magnus::embed::init() };
///
/// let pi = Float::from_f64(3.141592);
/// assert_eq!(pi.rationalize().to_string(), "392699/125000");
/// rb_assert!("r = 392699/125000r", r = pi.rationalize());
/// ```
pub fn rationalize(self) -> RRational {
unsafe { RRational::from_rb_value_unchecked(rb_flt_rationalize(self.as_rb_value())) }
Expand Down
60 changes: 56 additions & 4 deletions src/r_float.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,11 @@ impl Ruby {
}
}

/// A Value pointer to a RFloat struct, Ruby's internal representation of
/// A Value pointer to an RFloat struct, Ruby's internal representation of
/// high precision floating point numbers.
///
/// See also [`Float`] and [`Flonum`].
///
/// See the [`ReprValue`] trait for additional methods available on this type.
/// See [`Ruby`](Ruby#rfloat) for methods to create an `RFloat`.
#[derive(Clone, Copy)]
Expand All @@ -50,11 +52,25 @@ pub struct RFloat(NonZeroValue);

impl RFloat {
/// Return `Some(RFloat)` if `val` is a `RFloat`, `None` otherwise.
///
/// # Examples
///
/// ```
/// use magnus::{eval, RFloat};
/// # let _cleanup = unsafe { magnus::embed::init() };
///
/// assert!(RFloat::from_value(eval("1.7272337110188890e-77").unwrap()).is_some());
/// // can fit within a Flonum, so does not require an RFloat
/// assert!(RFloat::from_value(eval("1.7272337110188893e-77").unwrap()).is_none());
/// // not an RFloat
/// assert!(RFloat::from_value(eval("1").unwrap()).is_none());
/// ```
#[inline]
pub fn from_value(val: Value) -> Option<Self> {
unsafe {
(val.rb_type() == ruby_value_type::RUBY_T_FLOAT)
.then(|| Self(NonZeroValue::new_unchecked(val)))
(val.rb_type() == ruby_value_type::RUBY_T_FLOAT
&& (!cfg!(ruby_use_flonum) || !val.is_flonum()))
.then(|| Self(NonZeroValue::new_unchecked(val)))
}
}

Expand All @@ -66,12 +82,25 @@ impl RFloat {
/// Create a new `RFloat` from an `f64.`
///
/// Returns `Ok(RFloat)` if `n` requires a high precision float, otherwise
/// returns `Err(Fixnum)`.
/// returns `Err(Flonum)`.
///
/// # Panics
///
/// Panics if called from a non-Ruby thread. See [`Ruby::r_float_from_f64`]
/// for the non-panicking version.
///
/// # Examples
///
/// ```
/// use magnus::{rb_assert, RFloat};
/// # let _cleanup = unsafe { magnus::embed::init() };
///
/// let f = RFloat::from_f64(1.7272337110188890e-77).unwrap();
/// rb_assert!("f == 1.7272337110188890e-77", f);
///
/// // can fit within a Flonum, so does not require an RFloat
/// assert!(RFloat::from_f64(1.7272337110188893e-77).is_err());
/// ```
#[cfg(feature = "friendly-api")]
#[cfg(ruby_use_flonum)]
#[inline]
Expand All @@ -85,6 +114,19 @@ impl RFloat {
///
/// Panics if called from a non-Ruby thread. See [`Ruby::r_float_from_f64`]
/// for the non-panicking version.
///
/// # Examples
///
/// ```
/// use magnus::{rb_assert, RFloat};
/// # let _cleanup = unsafe { magnus::embed::init() };
///
/// let f = RFloat::from_f64(1.7272337110188893e-77).unwrap();
/// rb_assert!("f == 1.7272337110188893e-77", f);
///
/// let f = RFloat::from_f64(1.7272337110188890e-77).unwrap();
/// rb_assert!("f == 1.7272337110188890e-77", f);
/// ```
#[cfg(feature = "friendly-api")]
#[cfg(not(ruby_use_flonum))]
#[inline]
Expand All @@ -93,6 +135,16 @@ impl RFloat {
}

/// Convert `self` to a `f64`.
///
/// # Examples
///
/// ```
/// use magnus::{eval, RFloat};
/// # let _cleanup = unsafe { magnus::embed::init() };
///
/// let f: RFloat = eval("1.7272337110188890e-77").unwrap();
/// assert_eq!(f.to_f64(), 1.7272337110188890e-77);
/// ```
pub fn to_f64(self) -> f64 {
debug_assert_value!(self);
unsafe { rb_float_value(self.as_rb_value()) }
Expand Down
13 changes: 9 additions & 4 deletions src/value/flonum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ impl Ruby {
/// A Value known to be a flonum, Ruby's internal representation of lower
/// precision floating point numbers.
///
/// See also `Float`.
/// See also [`Float`] and [`RFloat`].
///
/// See the [`ReprValue`] trait for additional methods available on this type.
/// See [`Ruby`](Ruby#flonum) for methods to create a `Flonum`.
Expand All @@ -51,6 +51,8 @@ impl Flonum {
/// assert!(Flonum::from_value(eval("1.7272337110188893e-77").unwrap()).is_some());
/// // representable as a Float, but Flonum does not have enough precision
/// assert!(Flonum::from_value(eval("1.7272337110188890e-77").unwrap()).is_none());
/// // not a Flonum
/// assert!(Flonum::from_value(eval("1").unwrap()).is_none());
/// ```
#[inline]
pub fn from_value(val: Value) -> Option<Self> {
Expand Down Expand Up @@ -90,10 +92,12 @@ impl Flonum {
/// # Examples
///
/// ```
/// use magnus::Flonum;
/// use magnus::{rb_assert, Flonum};
/// # let _cleanup = unsafe { magnus::embed::init() };
///
/// assert!(Flonum::from_f64(1.7272337110188893e-77).is_ok());
/// let f = Flonum::from_f64(1.7272337110188893e-77).unwrap();
/// rb_assert!("f == 1.7272337110188893e-77", f);
///
/// // representable as a Float, but Flonum does not have enough precision
/// assert!(Flonum::from_f64(1.7272337110188890e-77).is_err());
/// ```
Expand All @@ -111,7 +115,8 @@ impl Flonum {
/// use magnus::{eval, Flonum};
/// # let _cleanup = unsafe { magnus::embed::init() };
///
/// assert_eq!(eval::<Flonum>("2.0").unwrap().to_f64(), 2.0);
/// let f: Flonum = eval("2.0").unwrap();
/// assert_eq!(f.to_f64(), 2.0);
/// ```
#[inline]
pub fn to_f64(self) -> f64 {
Expand Down

0 comments on commit 3800efd

Please sign in to comment.