Skip to main content

otter/types/terms/
float.rs

1//! [`Float`] — IEEE 754 double-precision Erlang floats.
2
3use core::marker::PhantomData;
4
5use crate::types::sealed::Sealed;
6use crate::types::{Env, Invariant, RawTerm, Term};
7
8/// An Erlang float. Always IEEE 754 double precision (heap-allocated in the
9/// BEAM even though the value is always `f64`).
10#[derive(Clone, Copy)]
11pub struct Float<'id> {
12    raw_term: RawTerm,
13    _id: Invariant<'id>,
14}
15
16impl<'id> Float<'id> {
17    #[crate::raw]
18    pub(crate) fn from_raw(raw_term: RawTerm) -> Self {
19        Self { raw_term, _id: PhantomData }
20    }
21
22    /// Construct a float term from an `f64` (`enif_make_double`).
23    ///
24    /// Returns `None` if `val` is not finite (NaN or infinity), which the BEAM
25    /// rejects with `badarg`. The check is done in Rust, so a rejected value
26    /// never calls into the BEAM and the env is never left with a pending
27    /// exception. A caller that wants a `badarg` can raise one from a `CallEnv`.
28    pub fn from_f64(env: impl Env<'id>, val: f64) -> Option<Self> {
29        if !val.is_finite() {
30            return None;
31        }
32        let raw_term = unsafe { enif_ffi::make_double(env.raw_env(), val) };
33        Some(Float { raw_term, _id: PhantomData })
34    }
35
36    /// Read back the `f64` (`enif_get_double`). `env` must carry the same brand
37    /// as this term.
38    pub fn to_f64(self, env: impl Env<'id>) -> f64 {
39        let mut val: f64 = 0.0;
40        // get_double fails only on a non-float; a Float is always a validated
41        // float term and every Erlang float is an f64, so this cannot fail.
42        let ok = unsafe { enif_ffi::get_double(env.raw_env(), self.raw_term, &mut val) };
43        assert!(ok != 0, "enif_get_double failed on a validated Float");
44        val
45    }
46}
47
48impl<'id> Sealed for Float<'id> {}
49
50impl<'id> Term<'id> for Float<'id> {
51    fn raw_term(self) -> RawTerm {
52        self.raw_term
53    }
54}
55
56impl PartialEq for Float<'_> {
57    fn eq(&self, other: &Self) -> bool {
58        unsafe { enif_ffi::is_identical(self.raw_term, other.raw_term) != 0 }
59    }
60}
61
62impl Eq for Float<'_> {}
63
64impl PartialOrd for Float<'_> {
65    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
66        Some(self.cmp(other))
67    }
68}
69
70impl Ord for Float<'_> {
71    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
72        let c = unsafe { enif_ffi::compare(self.raw_term, other.raw_term) };
73        c.cmp(&0)
74    }
75}
76
77impl std::fmt::Debug for Float<'_> {
78    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
79        write!(f, "Float")
80    }
81}