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
13pub 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 const PRIMITIVE: PrimitiveType;
35
36 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 AlignedBytes: AlignedBytes<Unaligned = Self::Bytes> + From<Self> + Into<Self>;
51
52 fn to_le_bytes(&self) -> Self::Bytes;
54
55 fn to_be_bytes(&self) -> Self::Bytes;
57
58 fn from_le_bytes(bytes: Self::Bytes) -> Self;
60
61 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#[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 #[inline]
118 pub fn new(days: i32, milliseconds: i32) -> Self {
119 Self(days, milliseconds)
120 }
121
122 #[inline]
124 pub fn days(&self) -> i32 {
125 self.0
126 }
127
128 #[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#[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 #[inline]
252 pub fn new(months: i32, days: i32, nanoseconds: i64) -> Self {
253 Self(months, days, nanoseconds)
254 }
255
256 #[inline]
258 pub fn months(&self) -> i32 {
259 self.0
260 }
261
262 #[inline]
264 pub fn days(&self) -> i32 {
265 self.1
266 }
267
268 #[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#[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#[inline]
456pub fn canonical_f16(x: f16) -> f16 {
457 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) } 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
504impl f16 {
506 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 #[inline]
517 pub const fn from_bits(bits: u16) -> f16 {
518 f16(bits)
519 }
520
521 #[inline]
523 pub const fn to_bits(self) -> u16 {
524 self.0
525 }
526
527 pub fn to_f32(self) -> f32 {
529 let i = self.0;
530 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 if half_exp == 0x7C00u32 {
541 if half_man == 0 {
543 let number = (half_sign << 16) | 0x7F80_0000u32;
544 return f32::from_bits(number);
545 } else {
546 let number = (half_sign << 16) | 0x7FC0_0000u32 | (half_man << 13);
548 return f32::from_bits(number);
549 }
550 }
551
552 let sign = half_sign << 16;
554 let unbiased_exp = ((half_exp as i32) >> 10) - 15;
556
557 if half_exp == 0 {
559 let e = (half_man as u16).leading_zeros() - 6;
561
562 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 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 pub fn from_f32(value: f32) -> Self {
576 let x: u32 = value.to_bits();
577
578 let sign = x & 0x8000_0000u32;
580 let exp = x & 0x7F80_0000u32;
581 let man = x & 0x007F_FFFFu32;
582
583 if exp == 0x7F80_0000u32 {
585 let nan_bit = if man == 0 { 0 } else { 0x0200u32 };
587 return f16(((sign >> 16) | 0x7C00u32 | nan_bit | (man >> 13)) as u16);
588 }
589
590 let half_sign = sign >> 16;
592 let unbiased_exp = ((exp >> 23) as i32) - 127;
594 let half_exp = unbiased_exp + 15;
595
596 if half_exp >= 0x1F {
598 return f16((half_sign | 0x7C00u32) as u16);
599 }
600
601 if half_exp <= 0 {
603 if 14 - half_exp > 24 {
605 return f16(half_sign as u16);
607 }
608 let man = man | 0x0080_0000u32;
610 let mut half_man = man >> (14 - half_exp);
611 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 return f16((half_sign | half_man) as u16);
618 }
619
620 let half_exp = (half_exp as u32) << 10;
622 let half_man = man >> 13;
623 let round_bit = 0x0000_1000u32;
625 if (man & round_bit) != 0 && (man & (3 * round_bit - 1)) != 0 {
626 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#[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 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 let f = f16::from_f32(7.1);
847 let diff = (f.to_f32() - 7.1f32).abs();
848 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}