havok_types/
half.rs

1use crate::lib::*;
2
3/// # f16
4/// Not an IEEE expression `f16`.
5///
6/// # C++ Info
7/// - name: `hkHalf`
8/// - type_size: ` 16`(x86)/` 16`(x86_64)
9///
10/// # Note
11/// - It is created by taking the upper 16 bits from f32.
12/// - Internally, it is the same as u16.
13#[allow(non_camel_case_types)]
14#[repr(transparent)]
15#[cfg_attr(feature = "json_schema", derive(schemars::JsonSchema))]
16#[cfg_attr(feature = "json_schema", schemars(transparent))]
17#[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
18pub struct f16(#[cfg_attr(feature = "json_schema", schemars(with = "f32"))] u16);
19
20impl f16 {
21    // NOTE: The bits in f64 and f32 are different, so if we get bits from f64, it will be an invalid value.
22    //
23    /// Creates a new [`f16`] from [`f32`].
24    #[inline]
25    pub const fn from_f32(f: f32) -> Self {
26        let bits = f.to_bits();
27        Self((bits >> 16) as u16) // Only the most significant 16bits are taken out.
28    }
29
30    /// Converts the [`f16`] to a 32-bit float.
31    #[inline]
32    pub const fn to_f32(self) -> f32 {
33        f32::from_bits((self.0 as u32) << 16)
34    }
35
36    /// Return as [`u16`].
37    #[inline]
38    pub const fn to_bits(self) -> u16 {
39        self.0
40    }
41
42    /// Creates a new [`f16`] from a little-endian byte array.
43    #[inline]
44    pub const fn from_le_bytes(bytes: [u8; 2]) -> Self {
45        Self(u16::from_le_bytes(bytes))
46    }
47
48    /// Creates a new `f16` from a big-endian byte array.
49    #[inline]
50    pub const fn from_be_bytes(bytes: [u8; 2]) -> Self {
51        Self(u16::from_be_bytes(bytes))
52    }
53
54    /// Return the memory representation of this integer as a byte array in little-endian byte order.
55    #[inline]
56    pub const fn to_le_bytes(self) -> [u8; 2] {
57        self.to_bits().to_le_bytes()
58    }
59
60    /// Return the memory representation of this integer as a byte array in big-endian byte order.
61    #[inline]
62    pub const fn to_be_bytes(self) -> [u8; 2] {
63        self.to_bits().to_be_bytes()
64    }
65}
66
67impl fmt::Display for f16 {
68    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
69        write!(f, "{:.06}", self.to_f32())
70    }
71}
72
73impl fmt::Debug for f16 {
74    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
75        write!(f, "{:.06}", self.to_f32())
76    }
77}
78
79#[cfg(feature = "serde")]
80const _: () = {
81    use super::f16;
82    use serde::{Deserialize, Deserializer, Serialize, Serializer};
83
84    impl Serialize for f16 {
85        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
86        where
87            S: Serializer,
88        {
89            self.to_f32().serialize(serializer)
90        }
91    }
92
93    impl<'de> Deserialize<'de> for f16 {
94        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
95        where
96            D: Deserializer<'de>,
97        {
98            let f = f32::deserialize(deserializer)?;
99            Ok(Self::from_f32(f))
100        }
101    }
102};
103
104#[test]
105fn test_half() {
106    {
107        let half = f16::from_f32(1.0);
108        const HALF_LE: [u8; 2] = [0x80, 0x3f];
109        const HALF_BE: [u8; 2] = [0x3f, 0x80];
110
111        assert_eq!(half.to_le_bytes(), HALF_LE);
112        assert_eq!(half.to_be_bytes(), HALF_BE);
113        assert_eq!(f16::from_le_bytes(HALF_LE), half);
114        assert_eq!(f16::from_be_bytes(HALF_BE), half);
115        assert_eq!(half.to_f32().to_bits(), (1.0_f32).to_bits()); // Check f32 at the bit level because of errors.
116        assert_eq!(half.to_string(), "1.000000");
117    }
118
119    {
120        let half = f16::from_f32(0.049805);
121        const HALF_LE: [u8; 2] = [0x4c, 0x3d];
122        const HALF_BE: [u8; 2] = [0x3d, 0x4c];
123        assert_eq!(half.to_le_bytes(), HALF_LE);
124        assert_eq!(half.to_be_bytes(), HALF_BE);
125        assert_eq!(f16::from_le_bytes(HALF_LE), half);
126        assert_eq!(f16::from_be_bytes(HALF_BE), half);
127        assert_eq!(half.to_f32().to_bits(), (0.049804688_f32).to_bits()); // Check f32 at the bit level because of errors.
128        assert_eq!(half.to_string(), "0.049805");
129    }
130}