polars_arrow/types/
native.rs

1use std::hash::{Hash, Hasher};
2use std::ops::Neg;
3use std::panic::RefUnwindSafe;
4
5use bytemuck::{Pod, Zeroable};
6use polars_utils::min_max::MinMax;
7use polars_utils::nulls::IsNull;
8use polars_utils::total_ord::{ToTotalOrd, TotalEq, TotalHash, TotalOrd, TotalOrdWrap};
9
10use super::aligned_bytes::*;
11use super::PrimitiveType;
12
13/// Sealed trait implemented by all physical types that can be allocated,
14/// serialized and deserialized by this crate.
15/// All O(N) allocations in this crate are done for this trait alone.
16pub trait NativeType:
17    super::private::Sealed
18    + Pod
19    + Send
20    + Sync
21    + Sized
22    + RefUnwindSafe
23    + std::fmt::Debug
24    + std::fmt::Display
25    + PartialEq
26    + Default
27    + Copy
28    + TotalOrd
29    + IsNull
30    + MinMax
31    + AlignedBytesCast<Self::AlignedBytes>
32{
33    /// The corresponding variant of [`PrimitiveType`].
34    const PRIMITIVE: PrimitiveType;
35
36    /// Type denoting its representation as bytes.
37    /// This is `[u8; N]` where `N = size_of::<T>`.
38    type Bytes: AsRef<[u8]>
39        + AsMut<[u8]>
40        + std::ops::Index<usize, Output = u8>
41        + std::ops::IndexMut<usize, Output = u8>
42        + for<'a> TryFrom<&'a [u8]>
43        + std::fmt::Debug
44        + Default
45        + IntoIterator<Item = u8>;
46
47    /// Type denoting its representation as aligned bytes.
48    ///
49    /// This is `[u8; N]` where `N = size_of::<Self>` and has alignment `align_of::<Self>`.
50    type AlignedBytes: AlignedBytes<Unaligned = Self::Bytes> + From<Self> + Into<Self>;
51
52    /// To bytes in little endian
53    fn to_le_bytes(&self) -> Self::Bytes;
54
55    /// To bytes in big endian
56    fn to_be_bytes(&self) -> Self::Bytes;
57
58    /// From bytes in little endian
59    fn from_le_bytes(bytes: Self::Bytes) -> Self;
60
61    /// From bytes in big endian
62    fn from_be_bytes(bytes: Self::Bytes) -> Self;
63}
64
65macro_rules! native_type {
66    ($type:ty, $aligned:ty, $primitive_type:expr) => {
67        impl NativeType for $type {
68            const PRIMITIVE: PrimitiveType = $primitive_type;
69
70            type Bytes = [u8; std::mem::size_of::<Self>()];
71            type AlignedBytes = $aligned;
72
73            #[inline]
74            fn to_le_bytes(&self) -> Self::Bytes {
75                Self::to_le_bytes(*self)
76            }
77
78            #[inline]
79            fn to_be_bytes(&self) -> Self::Bytes {
80                Self::to_be_bytes(*self)
81            }
82
83            #[inline]
84            fn from_le_bytes(bytes: Self::Bytes) -> Self {
85                Self::from_le_bytes(bytes)
86            }
87
88            #[inline]
89            fn from_be_bytes(bytes: Self::Bytes) -> Self {
90                Self::from_be_bytes(bytes)
91            }
92        }
93    };
94}
95
96native_type!(u8, Bytes1Alignment1, PrimitiveType::UInt8);
97native_type!(u16, Bytes2Alignment2, PrimitiveType::UInt16);
98native_type!(u32, Bytes4Alignment4, PrimitiveType::UInt32);
99native_type!(u64, Bytes8Alignment8, PrimitiveType::UInt64);
100native_type!(i8, Bytes1Alignment1, PrimitiveType::Int8);
101native_type!(i16, Bytes2Alignment2, PrimitiveType::Int16);
102native_type!(i32, Bytes4Alignment4, PrimitiveType::Int32);
103native_type!(i64, Bytes8Alignment8, PrimitiveType::Int64);
104native_type!(f32, Bytes4Alignment4, PrimitiveType::Float32);
105native_type!(f64, Bytes8Alignment8, PrimitiveType::Float64);
106native_type!(i128, Bytes16Alignment16, PrimitiveType::Int128);
107native_type!(u128, Bytes16Alignment16, PrimitiveType::UInt128);
108
109/// The in-memory representation of the DayMillisecond variant of arrow's "Interval" logical type.
110#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Zeroable, Pod)]
111#[allow(non_camel_case_types)]
112#[repr(C)]
113pub struct days_ms(pub i32, pub i32);
114
115impl days_ms {
116    /// A new [`days_ms`].
117    #[inline]
118    pub fn new(days: i32, milliseconds: i32) -> Self {
119        Self(days, milliseconds)
120    }
121
122    /// The number of days
123    #[inline]
124    pub fn days(&self) -> i32 {
125        self.0
126    }
127
128    /// The number of milliseconds
129    #[inline]
130    pub fn milliseconds(&self) -> i32 {
131        self.1
132    }
133}
134
135impl TotalEq for days_ms {
136    #[inline]
137    fn tot_eq(&self, other: &Self) -> bool {
138        self == other
139    }
140}
141
142impl TotalOrd for days_ms {
143    #[inline]
144    fn tot_cmp(&self, other: &Self) -> std::cmp::Ordering {
145        self.days()
146            .cmp(&other.days())
147            .then(self.milliseconds().cmp(&other.milliseconds()))
148    }
149}
150
151impl MinMax for days_ms {
152    fn nan_min_lt(&self, other: &Self) -> bool {
153        self < other
154    }
155
156    fn nan_max_lt(&self, other: &Self) -> bool {
157        self < other
158    }
159}
160
161impl NativeType for days_ms {
162    const PRIMITIVE: PrimitiveType = PrimitiveType::DaysMs;
163
164    type Bytes = [u8; 8];
165    type AlignedBytes = Bytes8Alignment4;
166
167    #[inline]
168    fn to_le_bytes(&self) -> Self::Bytes {
169        let days = self.0.to_le_bytes();
170        let ms = self.1.to_le_bytes();
171        let mut result = [0; 8];
172        result[0] = days[0];
173        result[1] = days[1];
174        result[2] = days[2];
175        result[3] = days[3];
176        result[4] = ms[0];
177        result[5] = ms[1];
178        result[6] = ms[2];
179        result[7] = ms[3];
180        result
181    }
182
183    #[inline]
184    fn to_be_bytes(&self) -> Self::Bytes {
185        let days = self.0.to_be_bytes();
186        let ms = self.1.to_be_bytes();
187        let mut result = [0; 8];
188        result[0] = days[0];
189        result[1] = days[1];
190        result[2] = days[2];
191        result[3] = days[3];
192        result[4] = ms[0];
193        result[5] = ms[1];
194        result[6] = ms[2];
195        result[7] = ms[3];
196        result
197    }
198
199    #[inline]
200    fn from_le_bytes(bytes: Self::Bytes) -> Self {
201        let mut days = [0; 4];
202        days[0] = bytes[0];
203        days[1] = bytes[1];
204        days[2] = bytes[2];
205        days[3] = bytes[3];
206        let mut ms = [0; 4];
207        ms[0] = bytes[4];
208        ms[1] = bytes[5];
209        ms[2] = bytes[6];
210        ms[3] = bytes[7];
211        Self(i32::from_le_bytes(days), i32::from_le_bytes(ms))
212    }
213
214    #[inline]
215    fn from_be_bytes(bytes: Self::Bytes) -> Self {
216        let mut days = [0; 4];
217        days[0] = bytes[0];
218        days[1] = bytes[1];
219        days[2] = bytes[2];
220        days[3] = bytes[3];
221        let mut ms = [0; 4];
222        ms[0] = bytes[4];
223        ms[1] = bytes[5];
224        ms[2] = bytes[6];
225        ms[3] = bytes[7];
226        Self(i32::from_be_bytes(days), i32::from_be_bytes(ms))
227    }
228}
229
230/// The in-memory representation of the MonthDayNano variant of the "Interval" logical type.
231#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Zeroable, Pod)]
232#[allow(non_camel_case_types)]
233#[repr(C)]
234pub struct months_days_ns(pub i32, pub i32, pub i64);
235
236impl IsNull for months_days_ns {
237    const HAS_NULLS: bool = false;
238    type Inner = months_days_ns;
239
240    fn is_null(&self) -> bool {
241        false
242    }
243
244    fn unwrap_inner(self) -> Self::Inner {
245        self
246    }
247}
248
249impl months_days_ns {
250    /// A new [`months_days_ns`].
251    #[inline]
252    pub fn new(months: i32, days: i32, nanoseconds: i64) -> Self {
253        Self(months, days, nanoseconds)
254    }
255
256    /// The number of months
257    #[inline]
258    pub fn months(&self) -> i32 {
259        self.0
260    }
261
262    /// The number of days
263    #[inline]
264    pub fn days(&self) -> i32 {
265        self.1
266    }
267
268    /// The number of nanoseconds
269    #[inline]
270    pub fn ns(&self) -> i64 {
271        self.2
272    }
273}
274
275impl TotalEq for months_days_ns {
276    #[inline]
277    fn tot_eq(&self, other: &Self) -> bool {
278        self == other
279    }
280}
281
282impl TotalOrd for months_days_ns {
283    #[inline]
284    fn tot_cmp(&self, other: &Self) -> std::cmp::Ordering {
285        self.months()
286            .cmp(&other.months())
287            .then(self.days().cmp(&other.days()))
288            .then(self.ns().cmp(&other.ns()))
289    }
290}
291
292impl MinMax for months_days_ns {
293    fn nan_min_lt(&self, other: &Self) -> bool {
294        self < other
295    }
296
297    fn nan_max_lt(&self, other: &Self) -> bool {
298        self < other
299    }
300}
301
302impl NativeType for months_days_ns {
303    const PRIMITIVE: PrimitiveType = PrimitiveType::MonthDayNano;
304
305    type Bytes = [u8; 16];
306    type AlignedBytes = Bytes16Alignment8;
307
308    #[inline]
309    fn to_le_bytes(&self) -> Self::Bytes {
310        let months = self.months().to_le_bytes();
311        let days = self.days().to_le_bytes();
312        let ns = self.ns().to_le_bytes();
313        let mut result = [0; 16];
314        result[0] = months[0];
315        result[1] = months[1];
316        result[2] = months[2];
317        result[3] = months[3];
318        result[4] = days[0];
319        result[5] = days[1];
320        result[6] = days[2];
321        result[7] = days[3];
322        (0..8).for_each(|i| {
323            result[8 + i] = ns[i];
324        });
325        result
326    }
327
328    #[inline]
329    fn to_be_bytes(&self) -> Self::Bytes {
330        let months = self.months().to_be_bytes();
331        let days = self.days().to_be_bytes();
332        let ns = self.ns().to_be_bytes();
333        let mut result = [0; 16];
334        result[0] = months[0];
335        result[1] = months[1];
336        result[2] = months[2];
337        result[3] = months[3];
338        result[4] = days[0];
339        result[5] = days[1];
340        result[6] = days[2];
341        result[7] = days[3];
342        (0..8).for_each(|i| {
343            result[8 + i] = ns[i];
344        });
345        result
346    }
347
348    #[inline]
349    fn from_le_bytes(bytes: Self::Bytes) -> Self {
350        let mut months = [0; 4];
351        months[0] = bytes[0];
352        months[1] = bytes[1];
353        months[2] = bytes[2];
354        months[3] = bytes[3];
355        let mut days = [0; 4];
356        days[0] = bytes[4];
357        days[1] = bytes[5];
358        days[2] = bytes[6];
359        days[3] = bytes[7];
360        let mut ns = [0; 8];
361        (0..8).for_each(|i| {
362            ns[i] = bytes[8 + i];
363        });
364        Self(
365            i32::from_le_bytes(months),
366            i32::from_le_bytes(days),
367            i64::from_le_bytes(ns),
368        )
369    }
370
371    #[inline]
372    fn from_be_bytes(bytes: Self::Bytes) -> Self {
373        let mut months = [0; 4];
374        months[0] = bytes[0];
375        months[1] = bytes[1];
376        months[2] = bytes[2];
377        months[3] = bytes[3];
378        let mut days = [0; 4];
379        days[0] = bytes[4];
380        days[1] = bytes[5];
381        days[2] = bytes[6];
382        days[3] = bytes[7];
383        let mut ns = [0; 8];
384        (0..8).for_each(|i| {
385            ns[i] = bytes[8 + i];
386        });
387        Self(
388            i32::from_be_bytes(months),
389            i32::from_be_bytes(days),
390            i64::from_be_bytes(ns),
391        )
392    }
393}
394
395impl IsNull for days_ms {
396    const HAS_NULLS: bool = false;
397    type Inner = days_ms;
398    fn is_null(&self) -> bool {
399        false
400    }
401    fn unwrap_inner(self) -> Self::Inner {
402        self
403    }
404}
405
406impl std::fmt::Display for days_ms {
407    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
408        write!(f, "{}d {}ms", self.days(), self.milliseconds())
409    }
410}
411
412impl std::fmt::Display for months_days_ns {
413    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
414        write!(f, "{}m {}d {}ns", self.months(), self.days(), self.ns())
415    }
416}
417
418impl Neg for days_ms {
419    type Output = Self;
420
421    #[inline(always)]
422    fn neg(self) -> Self::Output {
423        Self::new(-self.days(), -self.milliseconds())
424    }
425}
426
427impl Neg for months_days_ns {
428    type Output = Self;
429
430    #[inline(always)]
431    fn neg(self) -> Self::Output {
432        Self::new(-self.months(), -self.days(), -self.ns())
433    }
434}
435
436/// Type representation of the Float16 physical type
437#[derive(Copy, Clone, Default, Zeroable, Pod)]
438#[allow(non_camel_case_types)]
439#[repr(C)]
440pub struct f16(pub u16);
441
442impl PartialEq for f16 {
443    #[inline]
444    fn eq(&self, other: &f16) -> bool {
445        if self.is_nan() || other.is_nan() {
446            false
447        } else {
448            (self.0 == other.0) || ((self.0 | other.0) & 0x7FFFu16 == 0)
449        }
450    }
451}
452
453/// Converts an f32 into a canonical form, where -0 == 0 and all NaNs map to
454/// the same value.
455#[inline]
456pub fn canonical_f16(x: f16) -> f16 {
457    // zero out the sign bit if the f16 is zero.
458    let convert_zero = f16(x.0 & (0x7FFF | (u16::from(x.0 & 0x7FFF == 0) << 15)));
459    if convert_zero.is_nan() {
460        f16::from_bits(0x7c00) // Canonical quiet NaN.
461    } else {
462        convert_zero
463    }
464}
465
466impl TotalHash for f16 {
467    #[inline(always)]
468    fn tot_hash<H>(&self, state: &mut H)
469    where
470        H: Hasher,
471    {
472        canonical_f16(*self).to_bits().hash(state)
473    }
474}
475
476impl ToTotalOrd for f16 {
477    type TotalOrdItem = TotalOrdWrap<f16>;
478    type SourceItem = f16;
479
480    #[inline]
481    fn to_total_ord(&self) -> Self::TotalOrdItem {
482        TotalOrdWrap(*self)
483    }
484
485    #[inline]
486    fn peel_total_ord(ord_item: Self::TotalOrdItem) -> Self::SourceItem {
487        ord_item.0
488    }
489}
490
491impl IsNull for f16 {
492    const HAS_NULLS: bool = false;
493    type Inner = f16;
494
495    #[inline(always)]
496    fn is_null(&self) -> bool {
497        false
498    }
499    fn unwrap_inner(self) -> Self::Inner {
500        self
501    }
502}
503
504// see https://github.com/starkat99/half-rs/blob/main/src/binary16.rs
505impl f16 {
506    /// The difference between 1.0 and the next largest representable number.
507    pub const EPSILON: f16 = f16(0x1400u16);
508
509    #[inline]
510    #[must_use]
511    pub(crate) const fn is_nan(self) -> bool {
512        self.0 & 0x7FFFu16 > 0x7C00u16
513    }
514
515    /// Casts from u16.
516    #[inline]
517    pub const fn from_bits(bits: u16) -> f16 {
518        f16(bits)
519    }
520
521    /// Casts to u16.
522    #[inline]
523    pub const fn to_bits(self) -> u16 {
524        self.0
525    }
526
527    /// Casts this `f16` to `f32`
528    pub fn to_f32(self) -> f32 {
529        let i = self.0;
530        // Check for signed zero
531        if i & 0x7FFFu16 == 0 {
532            return f32::from_bits((i as u32) << 16);
533        }
534
535        let half_sign = (i & 0x8000u16) as u32;
536        let half_exp = (i & 0x7C00u16) as u32;
537        let half_man = (i & 0x03FFu16) as u32;
538
539        // Check for an infinity or NaN when all exponent bits set
540        if half_exp == 0x7C00u32 {
541            // Check for signed infinity if mantissa is zero
542            if half_man == 0 {
543                let number = (half_sign << 16) | 0x7F80_0000u32;
544                return f32::from_bits(number);
545            } else {
546                // NaN, keep current mantissa but also set most significiant mantissa bit
547                let number = (half_sign << 16) | 0x7FC0_0000u32 | (half_man << 13);
548                return f32::from_bits(number);
549            }
550        }
551
552        // Calculate single-precision components with adjusted exponent
553        let sign = half_sign << 16;
554        // Unbias exponent
555        let unbiased_exp = ((half_exp as i32) >> 10) - 15;
556
557        // Check for subnormals, which will be normalized by adjusting exponent
558        if half_exp == 0 {
559            // Calculate how much to adjust the exponent by
560            let e = (half_man as u16).leading_zeros() - 6;
561
562            // Rebias and adjust exponent
563            let exp = (127 - 15 - e) << 23;
564            let man = (half_man << (14 + e)) & 0x7F_FF_FFu32;
565            return f32::from_bits(sign | exp | man);
566        }
567
568        // Rebias exponent for a normalized normal
569        let exp = ((unbiased_exp + 127) as u32) << 23;
570        let man = (half_man & 0x03FFu32) << 13;
571        f32::from_bits(sign | exp | man)
572    }
573
574    /// Casts an `f32` into `f16`
575    pub fn from_f32(value: f32) -> Self {
576        let x: u32 = value.to_bits();
577
578        // Extract IEEE754 components
579        let sign = x & 0x8000_0000u32;
580        let exp = x & 0x7F80_0000u32;
581        let man = x & 0x007F_FFFFu32;
582
583        // Check for all exponent bits being set, which is Infinity or NaN
584        if exp == 0x7F80_0000u32 {
585            // Set mantissa MSB for NaN (and also keep shifted mantissa bits)
586            let nan_bit = if man == 0 { 0 } else { 0x0200u32 };
587            return f16(((sign >> 16) | 0x7C00u32 | nan_bit | (man >> 13)) as u16);
588        }
589
590        // The number is normalized, start assembling half precision version
591        let half_sign = sign >> 16;
592        // Unbias the exponent, then bias for half precision
593        let unbiased_exp = ((exp >> 23) as i32) - 127;
594        let half_exp = unbiased_exp + 15;
595
596        // Check for exponent overflow, return +infinity
597        if half_exp >= 0x1F {
598            return f16((half_sign | 0x7C00u32) as u16);
599        }
600
601        // Check for underflow
602        if half_exp <= 0 {
603            // Check mantissa for what we can do
604            if 14 - half_exp > 24 {
605                // No rounding possibility, so this is a full underflow, return signed zero
606                return f16(half_sign as u16);
607            }
608            // Don't forget about hidden leading mantissa bit when assembling mantissa
609            let man = man | 0x0080_0000u32;
610            let mut half_man = man >> (14 - half_exp);
611            // Check for rounding (see comment above functions)
612            let round_bit = 1 << (13 - half_exp);
613            if (man & round_bit) != 0 && (man & (3 * round_bit - 1)) != 0 {
614                half_man += 1;
615            }
616            // No exponent for subnormals
617            return f16((half_sign | half_man) as u16);
618        }
619
620        // Rebias the exponent
621        let half_exp = (half_exp as u32) << 10;
622        let half_man = man >> 13;
623        // Check for rounding (see comment above functions)
624        let round_bit = 0x0000_1000u32;
625        if (man & round_bit) != 0 && (man & (3 * round_bit - 1)) != 0 {
626            // Round it
627            f16(((half_sign | half_exp | half_man) + 1) as u16)
628        } else {
629            f16((half_sign | half_exp | half_man) as u16)
630        }
631    }
632}
633
634impl std::fmt::Debug for f16 {
635    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
636        write!(f, "{:?}", self.to_f32())
637    }
638}
639
640impl std::fmt::Display for f16 {
641    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
642        write!(f, "{}", self.to_f32())
643    }
644}
645
646impl TotalEq for f16 {
647    #[inline]
648    fn tot_eq(&self, other: &Self) -> bool {
649        if self.is_nan() {
650            other.is_nan()
651        } else {
652            self == other
653        }
654    }
655}
656
657impl TotalOrd for f16 {
658    #[inline]
659    fn tot_cmp(&self, _other: &Self) -> std::cmp::Ordering {
660        unimplemented!()
661    }
662}
663
664impl MinMax for f16 {
665    fn nan_min_lt(&self, _other: &Self) -> bool {
666        unimplemented!()
667    }
668
669    fn nan_max_lt(&self, _other: &Self) -> bool {
670        unimplemented!()
671    }
672}
673
674impl NativeType for f16 {
675    const PRIMITIVE: PrimitiveType = PrimitiveType::Float16;
676
677    type Bytes = [u8; 2];
678    type AlignedBytes = Bytes2Alignment2;
679
680    #[inline]
681    fn to_le_bytes(&self) -> Self::Bytes {
682        self.0.to_le_bytes()
683    }
684
685    #[inline]
686    fn to_be_bytes(&self) -> Self::Bytes {
687        self.0.to_be_bytes()
688    }
689
690    #[inline]
691    fn from_be_bytes(bytes: Self::Bytes) -> Self {
692        Self(u16::from_be_bytes(bytes))
693    }
694
695    #[inline]
696    fn from_le_bytes(bytes: Self::Bytes) -> Self {
697        Self(u16::from_le_bytes(bytes))
698    }
699}
700
701/// Physical representation of a decimal
702#[derive(Clone, Copy, Default, Eq, Hash, PartialEq, PartialOrd, Ord)]
703#[allow(non_camel_case_types)]
704#[repr(C)]
705pub struct i256(pub ethnum::I256);
706
707impl i256 {
708    /// Returns a new [`i256`] from two `i128`.
709    pub fn from_words(hi: i128, lo: i128) -> Self {
710        Self(ethnum::I256::from_words(hi, lo))
711    }
712}
713
714impl IsNull for i256 {
715    const HAS_NULLS: bool = false;
716    type Inner = i256;
717    #[inline(always)]
718    fn is_null(&self) -> bool {
719        false
720    }
721    fn unwrap_inner(self) -> Self::Inner {
722        self
723    }
724}
725
726impl Neg for i256 {
727    type Output = Self;
728
729    #[inline]
730    fn neg(self) -> Self::Output {
731        let (a, b) = self.0.into_words();
732        Self(ethnum::I256::from_words(-a, b))
733    }
734}
735
736impl std::fmt::Debug for i256 {
737    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
738        write!(f, "{:?}", self.0)
739    }
740}
741
742impl std::fmt::Display for i256 {
743    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
744        write!(f, "{}", self.0)
745    }
746}
747
748unsafe impl Pod for i256 {}
749unsafe impl Zeroable for i256 {}
750
751impl TotalEq for i256 {
752    #[inline]
753    fn tot_eq(&self, other: &Self) -> bool {
754        self == other
755    }
756}
757
758impl TotalOrd for i256 {
759    #[inline]
760    fn tot_cmp(&self, other: &Self) -> std::cmp::Ordering {
761        self.cmp(other)
762    }
763}
764
765impl MinMax for i256 {
766    fn nan_min_lt(&self, other: &Self) -> bool {
767        self < other
768    }
769
770    fn nan_max_lt(&self, other: &Self) -> bool {
771        self < other
772    }
773}
774
775impl NativeType for i256 {
776    const PRIMITIVE: PrimitiveType = PrimitiveType::Int256;
777
778    type Bytes = [u8; 32];
779    type AlignedBytes = Bytes32Alignment16;
780
781    #[inline]
782    fn to_le_bytes(&self) -> Self::Bytes {
783        let mut bytes = [0u8; 32];
784        let (a, b) = self.0.into_words();
785        let a = a.to_le_bytes();
786        (0..16).for_each(|i| {
787            bytes[i] = a[i];
788        });
789
790        let b = b.to_le_bytes();
791        (0..16).for_each(|i| {
792            bytes[i + 16] = b[i];
793        });
794
795        bytes
796    }
797
798    #[inline]
799    fn to_be_bytes(&self) -> Self::Bytes {
800        let mut bytes = [0u8; 32];
801        let (a, b) = self.0.into_words();
802
803        let a = a.to_be_bytes();
804        (0..16).for_each(|i| {
805            bytes[i] = a[i];
806        });
807
808        let b = b.to_be_bytes();
809        (0..16).for_each(|i| {
810            bytes[i + 16] = b[i];
811        });
812
813        bytes
814    }
815
816    #[inline]
817    fn from_be_bytes(bytes: Self::Bytes) -> Self {
818        let (a, b) = bytes.split_at(16);
819        let a: [u8; 16] = a.try_into().unwrap();
820        let b: [u8; 16] = b.try_into().unwrap();
821        let a = i128::from_be_bytes(a);
822        let b = i128::from_be_bytes(b);
823        Self(ethnum::I256::from_words(a, b))
824    }
825
826    #[inline]
827    fn from_le_bytes(bytes: Self::Bytes) -> Self {
828        let (b, a) = bytes.split_at(16);
829        let a: [u8; 16] = a.try_into().unwrap();
830        let b: [u8; 16] = b.try_into().unwrap();
831        let a = i128::from_le_bytes(a);
832        let b = i128::from_le_bytes(b);
833        Self(ethnum::I256::from_words(a, b))
834    }
835}
836
837#[cfg(test)]
838mod test {
839    use super::*;
840    #[test]
841    fn test_f16_to_f32() {
842        let f = f16::from_f32(7.0);
843        assert_eq!(f.to_f32(), 7.0f32);
844
845        // 7.1 is NOT exactly representable in 16-bit, it's rounded
846        let f = f16::from_f32(7.1);
847        let diff = (f.to_f32() - 7.1f32).abs();
848        // diff must be <= 4 * EPSILON, as 7 has two more significant bits than 1
849        assert!(diff <= 4.0 * f16::EPSILON.to_f32());
850
851        assert_eq!(f16(0x0000_0001).to_f32(), 2.0f32.powi(-24));
852        assert_eq!(f16(0x0000_0005).to_f32(), 5.0 * 2.0f32.powi(-24));
853
854        assert_eq!(f16(0x0000_0001), f16::from_f32(2.0f32.powi(-24)));
855        assert_eq!(f16(0x0000_0005), f16::from_f32(5.0 * 2.0f32.powi(-24)));
856
857        assert_eq!(format!("{}", f16::from_f32(7.0)), "7".to_string());
858        assert_eq!(format!("{:?}", f16::from_f32(7.0)), "7.0".to_string());
859    }
860}