Skip to main content

cbor_core/ext/
half.rs

1use half::f16;
2
3use crate::{Error, Float, Result, Value, float::Inner};
4
5impl Float {
6    /// Convert to `half::f16`.
7    ///
8    /// Returns `Err(Precision)` for f32 or f64-width values.
9    pub const fn to_f16(self) -> Result<f16> {
10        match self.0 {
11            Inner::F16(bits) => Ok(f16::from_bits(bits)),
12            Inner::F32(_) => Err(Error::Precision),
13            Inner::F64(_) => Err(Error::Precision),
14        }
15    }
16}
17
18impl From<f16> for Float {
19    fn from(value: f16) -> Self {
20        Self(Inner::F16(value.to_bits()))
21    }
22}
23
24impl Value {
25    /// Convert to `half::f16`.
26    ///
27    /// Returns `Err(Precision)` for f32 or f64-width values.
28    pub fn to_f16(&self) -> Result<f16> {
29        match self {
30            Self::Float(float) => float.to_f16(),
31            Self::Tag(_number, content) => content.untagged().to_f16(),
32            _ => Err(Error::IncompatibleType(self.data_type())),
33        }
34    }
35}
36
37impl From<f16> for Value {
38    fn from(value: f16) -> Self {
39        Self::Float(value.into())
40    }
41}
42
43impl TryFrom<Value> for f16 {
44    type Error = Error;
45    fn try_from(value: Value) -> Result<Self> {
46        value.to_f16()
47    }
48}
49
50// ---------------------------------------------------------------------------
51// Tests
52// ---------------------------------------------------------------------------
53
54#[cfg(test)]
55mod tests {
56    use half::f16;
57
58    use crate::{DataType, Error, Float, Value, float::Inner};
59
60    // -------------------------------------------------------------------------
61    // Float::to_f16
62    // -------------------------------------------------------------------------
63
64    #[test]
65    fn float_to_f16_from_f16_storage() {
66        let f: Float = f16::from_f32(1.0).into();
67        assert_eq!(f.to_f16(), Ok(f16::from_f32(1.0)));
68    }
69
70    #[test]
71    fn float_to_f16_rejects_f32() {
72        // 1e10 fits in f32 but not f16
73        let f: Float = 1e10_f32.into();
74        assert!(matches!(f.0, Inner::F32(_)));
75        assert_eq!(f.to_f16(), Err(Error::Precision));
76    }
77
78    #[test]
79    fn float_to_f16_rejects_f64() {
80        // 1e100 requires f64
81        let f: Float = 1e100_f64.into();
82        assert!(matches!(f.0, Inner::F64(_)));
83        assert_eq!(f.to_f16(), Err(Error::Precision));
84    }
85
86    #[test]
87    fn float_to_f16_zero() {
88        let f: Float = f16::ZERO.into();
89        assert_eq!(f.to_f16().unwrap().to_bits(), f16::ZERO.to_bits());
90        assert_eq!(f.to_f32().unwrap().to_bits(), 0.0_f32.to_bits());
91        assert_eq!(f.to_f64().to_bits(), 0.0_f64.to_bits());
92    }
93
94    #[test]
95    fn float_to_f16_neg_zero() {
96        let f: Float = f16::NEG_ZERO.into();
97        assert_eq!(f.to_f16().unwrap().to_bits(), f16::NEG_ZERO.to_bits());
98        assert_eq!(f.to_f32().unwrap().to_bits(), (-0.0_f32).to_bits());
99        assert_eq!(f.to_f64().to_bits(), (-0.0_f64).to_bits());
100    }
101
102    #[test]
103    fn float_to_f16_infinity() {
104        let f: Float = f16::INFINITY.into();
105        assert_eq!(f.to_f16().unwrap(), f16::INFINITY);
106        assert_eq!(f.to_f32().unwrap(), f32::INFINITY);
107        assert_eq!(f.to_f64(), f64::INFINITY);
108    }
109
110    #[test]
111    fn float_to_f16_nan() {
112        let f: Float = f16::NAN.into();
113        assert!(f.to_f16().unwrap().is_nan());
114        assert!(f.to_f32().unwrap().is_nan());
115        assert!(f.to_f64().is_nan());
116    }
117
118    // -------------------------------------------------------------------------
119    // From<f16> for Float
120    // -------------------------------------------------------------------------
121
122    #[test]
123    fn from_f16_stores_as_f16_bits() {
124        let v = f16::from_f32(42.0);
125        let f: Float = v.into();
126        assert!(matches!(f.0, Inner::F16(_)));
127    }
128
129    #[test]
130    fn from_f16_roundtrips() {
131        for bits in 0_u16..=0x7fff {
132            let v = f16::from_bits(bits);
133            if v.is_nan() {
134                continue;
135            }
136            let f: Float = v.into();
137            assert_eq!(
138                f.to_f16().unwrap().to_bits(),
139                bits,
140                "roundtrip failed for bits 0x{bits:04x}"
141            );
142        }
143    }
144
145    // -------------------------------------------------------------------------
146    // Value::to_f16
147    // -------------------------------------------------------------------------
148
149    #[test]
150    fn value_to_f16_from_float_value() {
151        let val: Value = f16::from_f32(1.5).into();
152        assert_eq!(val.to_f16(), Ok(f16::from_f32(1.5)));
153    }
154
155    #[test]
156    fn value_to_f16_incompatible_type() {
157        let val = Value::Unsigned(42);
158        assert_eq!(val.to_f16(), Err(Error::IncompatibleType(DataType::Int)));
159    }
160
161    #[test]
162    fn value_to_f16_string_is_incompatible() {
163        let val = Value::from("hello");
164        assert_eq!(val.to_f16(), Err(Error::IncompatibleType(DataType::Text)));
165    }
166
167    #[test]
168    fn value_to_f16_f32_precision_error() {
169        let val: Value = 1e10_f32.into();
170        assert_eq!(val.to_f16(), Err(Error::Precision));
171    }
172
173    // -------------------------------------------------------------------------
174    // From<f16> for Value
175    // -------------------------------------------------------------------------
176
177    #[test]
178    fn value_from_f16_is_float_variant() {
179        let val: Value = f16::from_f32(2.0).into();
180        assert!(matches!(val, Value::Float(_)));
181    }
182
183    // -------------------------------------------------------------------------
184    // TryFrom<Value> for f16
185    // -------------------------------------------------------------------------
186
187    #[test]
188    fn try_from_value_for_f16_ok() {
189        let val: Value = f16::from_f32(3.41).into();
190        let result = f16::try_from(val).unwrap();
191        assert_eq!(result, f16::from_f32(3.41));
192    }
193
194    #[test]
195    fn try_from_value_for_f16_err() {
196        let val = Value::Unsigned(1);
197        assert_eq!(f16::try_from(val), Err(Error::IncompatibleType(DataType::Int)));
198    }
199}