Skip to main content

cbor_core/ext/
half.rs

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