@@ -9,7 +9,9 @@ use num_traits::NumCast;
99use paste:: paste;
1010use vortex_dtype:: half:: f16;
1111use vortex_dtype:: { NativePType , PType , ToBytes } ;
12- use vortex_error:: { VortexError , VortexExpect , VortexResult , vortex_bail, vortex_err} ;
12+ use vortex_error:: {
13+ VortexError , VortexExpect , VortexResult , vortex_bail, vortex_ensure, vortex_err,
14+ } ;
1315
1416/// A primitive value that can represent any primitive type supported by Vortex.
1517///
@@ -444,8 +446,20 @@ impl CoercePValue for f16 {
444446 match value {
445447 PValue :: U8 ( u) => Ok ( Self :: from_bits ( u as u16 ) ) ,
446448 PValue :: U16 ( u) => Ok ( Self :: from_bits ( u) ) ,
447- PValue :: U32 ( u) => Ok ( Self :: from_bits ( u as u16 ) ) ,
448- PValue :: U64 ( u) => Ok ( Self :: from_bits ( u as u16 ) ) ,
449+ PValue :: U32 ( u) => {
450+ vortex_ensure ! (
451+ u <= u16 :: MAX as u32 ,
452+ "Cannot coerce U32 value to f16: value out of range"
453+ ) ;
454+ Ok ( Self :: from_bits ( u as u16 ) )
455+ }
456+ PValue :: U64 ( u) => {
457+ vortex_ensure ! (
458+ u <= u16 :: MAX as u64 ,
459+ "Cannot coerce U64 value to f16: value out of range"
460+ ) ;
461+ Ok ( Self :: from_bits ( u as u16 ) )
462+ }
449463 PValue :: F16 ( u) => Ok ( u) ,
450464 PValue :: F32 ( f) => {
451465 <Self as NumCast >:: from ( f) . ok_or_else ( || vortex_err ! ( "Cannot convert f32 to f16" ) )
@@ -468,7 +482,13 @@ impl CoercePValue for f32 {
468482 PValue :: U8 ( u) => Ok ( Self :: from_bits ( u as u32 ) ) ,
469483 PValue :: U16 ( u) => Ok ( Self :: from_bits ( u as u32 ) ) ,
470484 PValue :: U32 ( u) => Ok ( Self :: from_bits ( u) ) ,
471- PValue :: U64 ( u) => Ok ( Self :: from_bits ( u as u32 ) ) ,
485+ PValue :: U64 ( u) => {
486+ vortex_ensure ! (
487+ u <= u32 :: MAX as u64 ,
488+ "Cannot coerce U64 value to f32: value out of range"
489+ ) ;
490+ Ok ( Self :: from_bits ( u as u32 ) )
491+ }
472492 PValue :: F16 ( f) => {
473493 <Self as NumCast >:: from ( f) . ok_or_else ( || vortex_err ! ( "Cannot convert f16 to f32" ) )
474494 }
@@ -511,10 +531,11 @@ mod test {
511531 use std:: cmp:: Ordering ;
512532 use std:: collections:: HashSet ;
513533
514- use vortex_dtype:: PType ;
515534 use vortex_dtype:: half:: f16;
535+ use vortex_dtype:: { PType , ToBytes } ;
516536
517537 use crate :: PValue ;
538+ use crate :: pvalue:: CoercePValue ;
518539
519540 #[ test]
520541 pub fn test_is_instance_of ( ) {
@@ -781,8 +802,6 @@ mod test {
781802
782803 #[ test]
783804 fn test_to_le_bytes ( ) {
784- use vortex_dtype:: ToBytes ;
785-
786805 assert_eq ! ( PValue :: U8 ( 0x12 ) . to_le_bytes( ) , & [ 0x12 ] ) ;
787806 assert_eq ! ( PValue :: U16 ( 0x1234 ) . to_le_bytes( ) , & [ 0x34 , 0x12 ] ) ;
788807 assert_eq ! (
@@ -821,8 +840,6 @@ mod test {
821840
822841 #[ test]
823842 fn test_coerce_pvalue ( ) {
824- use super :: CoercePValue ;
825-
826843 // Test integer coercion
827844 assert_eq ! ( u32 :: coerce( PValue :: U16 ( 42 ) ) . unwrap( ) , 42u32 ) ;
828845 assert_eq ! ( i64 :: coerce( PValue :: I32 ( -42 ) ) . unwrap( ) , -42i64 ) ;
@@ -834,4 +851,64 @@ mod test {
834851 1.0f64
835852 ) ;
836853 }
854+
855+ #[ test]
856+ fn test_coerce_f16_beyond_u16_max ( ) {
857+ // Test U32 to f16 coercion within valid range
858+ assert ! ( f16:: coerce( PValue :: U32 ( u16 :: MAX as u32 ) ) . is_ok( ) ) ;
859+ assert_eq ! (
860+ f16:: coerce( PValue :: U32 ( 0x3C00 ) ) . unwrap( ) ,
861+ f16:: from_bits( 0x3C00 ) // 1.0 in f16
862+ ) ;
863+
864+ // Test U32 to f16 coercion beyond u16::MAX - should fail
865+ assert ! ( f16:: coerce( PValue :: U32 ( ( u16 :: MAX as u32 ) + 1 ) ) . is_err( ) ) ;
866+ assert ! ( f16:: coerce( PValue :: U32 ( u32 :: MAX ) ) . is_err( ) ) ;
867+
868+ // Test U64 to f16 coercion within valid range
869+ assert ! ( f16:: coerce( PValue :: U64 ( u16 :: MAX as u64 ) ) . is_ok( ) ) ;
870+ assert_eq ! (
871+ f16:: coerce( PValue :: U64 ( 0x3C00 ) ) . unwrap( ) ,
872+ f16:: from_bits( 0x3C00 ) // 1.0 in f16
873+ ) ;
874+
875+ // Test U64 to f16 coercion beyond u16::MAX - should fail
876+ assert ! ( f16:: coerce( PValue :: U64 ( ( u16 :: MAX as u64 ) + 1 ) ) . is_err( ) ) ;
877+ assert ! ( f16:: coerce( PValue :: U64 ( u32 :: MAX as u64 ) ) . is_err( ) ) ;
878+ assert ! ( f16:: coerce( PValue :: U64 ( u64 :: MAX ) ) . is_err( ) ) ;
879+ }
880+
881+ #[ test]
882+ fn test_coerce_f32_beyond_u32_max ( ) {
883+ // Test U64 to f32 coercion within valid range
884+ assert ! ( f32 :: coerce( PValue :: U64 ( u32 :: MAX as u64 ) ) . is_ok( ) ) ;
885+ assert_eq ! (
886+ f32 :: coerce( PValue :: U64 ( 0x3f800000 ) ) . unwrap( ) ,
887+ 1.0f32 // 0x3f800000 is 1.0 in f32
888+ ) ;
889+
890+ // Test U64 to f32 coercion beyond u32::MAX - should fail
891+ assert ! ( f32 :: coerce( PValue :: U64 ( ( u32 :: MAX as u64 ) + 1 ) ) . is_err( ) ) ;
892+ assert ! ( f32 :: coerce( PValue :: U64 ( u64 :: MAX ) ) . is_err( ) ) ;
893+
894+ // Test smaller types still work
895+ assert ! ( f32 :: coerce( PValue :: U8 ( 255 ) ) . is_ok( ) ) ;
896+ assert ! ( f32 :: coerce( PValue :: U16 ( u16 :: MAX ) ) . is_ok( ) ) ;
897+ assert ! ( f32 :: coerce( PValue :: U32 ( u32 :: MAX ) ) . is_ok( ) ) ;
898+ }
899+
900+ #[ test]
901+ fn test_coerce_f64_all_unsigned ( ) {
902+ // Test f64 can accept all unsigned integer values as bit patterns
903+ assert ! ( f64 :: coerce( PValue :: U8 ( u8 :: MAX ) ) . is_ok( ) ) ;
904+ assert ! ( f64 :: coerce( PValue :: U16 ( u16 :: MAX ) ) . is_ok( ) ) ;
905+ assert ! ( f64 :: coerce( PValue :: U32 ( u32 :: MAX ) ) . is_ok( ) ) ;
906+ assert ! ( f64 :: coerce( PValue :: U64 ( u64 :: MAX ) ) . is_ok( ) ) ;
907+
908+ // Verify specific bit patterns
909+ assert_eq ! (
910+ f64 :: coerce( PValue :: U64 ( 0x3ff0000000000000 ) ) . unwrap( ) ,
911+ 1.0f64 // 0x3ff0000000000000 is 1.0 in f64
912+ ) ;
913+ }
837914}
0 commit comments