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