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    /// 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
50impl From<f16> for ValueKey<'_> {
51    fn from(value: f16) -> Self {
52        Float::from(value).into()
53    }
54}
55
56// ---------------------------------------------------------------------------
57// Tests
58// ---------------------------------------------------------------------------
59
60#[cfg(test)]
61mod tests {
62    use half::f16;
63
64    use crate::{DataType, Error, Float, Value, float::Inner};
65
66    // -------------------------------------------------------------------------
67    // Float::to_f16
68    // -------------------------------------------------------------------------
69
70    #[test]
71    fn float_to_f16_from_f16_storage() {
72        let f: Float = f16::from_f32(1.0).into();
73        assert_eq!(f.to_f16(), Ok(f16::from_f32(1.0)));
74    }
75
76    #[test]
77    fn float_to_f16_rejects_f32() {
78        // 1e10 fits in f32 but not f16
79        let f: Float = 1e10_f32.into();
80        assert!(matches!(f.0, Inner::F32(_)));
81        assert_eq!(f.to_f16(), Err(Error::Precision));
82    }
83
84    #[test]
85    fn float_to_f16_rejects_f64() {
86        // 1e100 requires f64
87        let f: Float = 1e100_f64.into();
88        assert!(matches!(f.0, Inner::F64(_)));
89        assert_eq!(f.to_f16(), Err(Error::Precision));
90    }
91
92    #[test]
93    fn float_to_f16_zero() {
94        let f: Float = f16::ZERO.into();
95        assert_eq!(f.to_f16().unwrap().to_bits(), f16::ZERO.to_bits());
96        assert_eq!(f.to_f32().unwrap().to_bits(), 0.0_f32.to_bits());
97        assert_eq!(f.to_f64().to_bits(), 0.0_f64.to_bits());
98    }
99
100    #[test]
101    fn float_to_f16_neg_zero() {
102        let f: Float = f16::NEG_ZERO.into();
103        assert_eq!(f.to_f16().unwrap().to_bits(), f16::NEG_ZERO.to_bits());
104        assert_eq!(f.to_f32().unwrap().to_bits(), (-0.0_f32).to_bits());
105        assert_eq!(f.to_f64().to_bits(), (-0.0_f64).to_bits());
106    }
107
108    #[test]
109    fn float_to_f16_infinity() {
110        let f: Float = f16::INFINITY.into();
111        assert_eq!(f.to_f16().unwrap(), f16::INFINITY);
112        assert_eq!(f.to_f32().unwrap(), f32::INFINITY);
113        assert_eq!(f.to_f64(), f64::INFINITY);
114    }
115
116    #[test]
117    fn float_to_f16_nan() {
118        let f: Float = f16::NAN.into();
119        assert!(f.to_f16().unwrap().is_nan());
120        assert!(f.to_f32().unwrap().is_nan());
121        assert!(f.to_f64().is_nan());
122    }
123
124    // -------------------------------------------------------------------------
125    // From<f16> for Float
126    // -------------------------------------------------------------------------
127
128    #[test]
129    fn from_f16_stores_as_f16_bits() {
130        let v = f16::from_f32(42.0);
131        let f: Float = v.into();
132        assert!(matches!(f.0, Inner::F16(_)));
133    }
134
135    #[test]
136    fn from_f16_roundtrips() {
137        for bits in 0_u16..=0x7fff {
138            let v = f16::from_bits(bits);
139            if v.is_nan() {
140                continue;
141            }
142            let f: Float = v.into();
143            assert_eq!(
144                f.to_f16().unwrap().to_bits(),
145                bits,
146                "roundtrip failed for bits 0x{bits:04x}"
147            );
148        }
149    }
150
151    // -------------------------------------------------------------------------
152    // Value::to_f16
153    // -------------------------------------------------------------------------
154
155    #[test]
156    fn value_to_f16_from_float_value() {
157        let val: Value = f16::from_f32(1.5).into();
158        assert_eq!(val.to_f16(), Ok(f16::from_f32(1.5)));
159    }
160
161    #[test]
162    fn value_to_f16_incompatible_type() {
163        let val = Value::Unsigned(42);
164        assert_eq!(val.to_f16(), Err(Error::IncompatibleType(DataType::Int)));
165    }
166
167    #[test]
168    fn value_to_f16_string_is_incompatible() {
169        let val = Value::from("hello");
170        assert_eq!(val.to_f16(), Err(Error::IncompatibleType(DataType::Text)));
171    }
172
173    #[test]
174    fn value_to_f16_f32_precision_error() {
175        let val: Value = 1e10_f32.into();
176        assert_eq!(val.to_f16(), Err(Error::Precision));
177    }
178
179    // -------------------------------------------------------------------------
180    // From<f16> for Value
181    // -------------------------------------------------------------------------
182
183    #[test]
184    fn value_from_f16_is_float_variant() {
185        let val: Value = f16::from_f32(2.0).into();
186        assert!(matches!(val, Value::Float(_)));
187    }
188
189    // -------------------------------------------------------------------------
190    // TryFrom<Value> for f16
191    // -------------------------------------------------------------------------
192
193    #[test]
194    fn try_from_value_for_f16_ok() {
195        let val: Value = f16::from_f32(3.41).into();
196        let result = f16::try_from(val).unwrap();
197        assert_eq!(result, f16::from_f32(3.41));
198    }
199
200    #[test]
201    fn try_from_value_for_f16_err() {
202        let val = Value::Unsigned(1);
203        assert_eq!(f16::try_from(val), Err(Error::IncompatibleType(DataType::Int)));
204    }
205}