otter/codec/float.rs
1//! `Encoder`/`Decoder` for Rust's floating-point types.
2//!
3//! Erlang floats are always finite IEEE-754 doubles. Encoding therefore rejects
4//! a non-finite value with [`CodecError::NotFinite`] (surfaced as `badret` on a
5//! NIF return) rather than handing NaN/infinity to the BEAM. `f32` widens
6//! losslessly to `f64` on the way out; on the way in it range-checks against the
7//! finite `f32` range — a value larger than `f32::MAX` would narrow to infinity,
8//! so it is rejected with [`CodecError::FloatRange`] (accepting ordinary
9//! rounding within range).
10
11use crate::codec::{CodecError, Decoder, Encoder};
12use crate::types::{AnyTerm, Env, Float};
13
14/// Rejects a non-finite value (NaN/infinity) with
15/// [`NotFinite`](CodecError::NotFinite); otherwise `enif_make_double`.
16impl<'id> Encoder<'id> for f64 {
17 fn encode(&self, env: impl Env<'id>) -> Result<AnyTerm<'id>, CodecError> {
18 Float::from_f64(env, *self).ok_or(CodecError::NotFinite)?.encode(env)
19 }
20}
21
22/// Reads a float term (`enif_get_double`); an Erlang float is always finite, so
23/// this never fails on range. [`WrongType`](CodecError::WrongType) if not a float.
24impl<'id> Decoder<'id> for f64 {
25 fn decode(term: AnyTerm<'id>, env: impl Env<'id>) -> Result<Self, CodecError> {
26 Ok(Float::decode(term, env)?.to_f64(env))
27 }
28}
29
30/// Widens to `f64` and encodes, rejecting non-finite values with
31/// [`NotFinite`](CodecError::NotFinite).
32impl<'id> Encoder<'id> for f32 {
33 fn encode(&self, env: impl Env<'id>) -> Result<AnyTerm<'id>, CodecError> {
34 Float::from_f64(env, f64::from(*self)).ok_or(CodecError::NotFinite)?.encode(env)
35 }
36}
37
38/// Reads as `f64` and narrows; a magnitude beyond `f32::MAX` would round to
39/// infinity, so it is rejected with [`FloatRange`](CodecError::FloatRange).
40impl<'id> Decoder<'id> for f32 {
41 fn decode(term: AnyTerm<'id>, env: impl Env<'id>) -> Result<Self, CodecError> {
42 let val = Float::decode(term, env)?.to_f64(env);
43 // An Erlang float is always finite, so the only failure is magnitude:
44 // narrowing a value beyond f32::MAX would produce infinity.
45 if val.abs() > f64::from(f32::MAX) {
46 return Err(CodecError::FloatRange);
47 }
48 Ok(val as f32)
49 }
50}