@@ -773,6 +773,131 @@ def test_locale_independence(self):
773773 with pytest .raises (ValueError ):
774774 arr_comma .astype (QuadPrecDType ())
775775
776+ @pytest .mark .parametrize ("input_str,description" , [
777+ (" 1.23 " , "space - leading and trailing" ),
778+ ("\t 1.23\t " , "tab - leading and trailing" ),
779+ ("\n 1.23\n " , "newline - leading and trailing" ),
780+ ("\r 1.23\r " , "carriage return - leading and trailing" ),
781+ ("\v 1.23\v " , "vertical tab - leading and trailing" ),
782+ ("\f 1.23\f " , "form feed - leading and trailing" ),
783+ (" \t \n \r \v \f 1.23 \t \n \r \v \f " , "all 6 whitespace chars - mixed" ),
784+ ("\t \t \t 3.14\t \t \t " , "multiple tabs" ),
785+ (" inf " , "infinity with spaces" ),
786+ ("\t \t -inf\t \t " , "negative infinity with tabs" ),
787+ ("\n \n nan\n \n " , "nan with newlines" ),
788+ ("\r \r -nan\r \r " , "negative nan with carriage returns" ),
789+ ("\v \v 1e10\v \v " , "scientific notation with vertical tabs" ),
790+ ("\f \f -1.23e-45\f \f " , "negative scientific with form feeds" ),
791+ ])
792+ def test_all_six_whitespace_characters (self , input_str , description ):
793+ """Test all 6 ASCII whitespace characters (space, tab, newline, carriage return, vertical tab, form feed)
794+
795+ This tests the ascii_isspace() helper function in casts.cpp which matches
796+ CPython's Py_ISSPACE and NumPy's NumPyOS_ascii_isspace behavior.
797+ The 6 characters are: 0x09(\t ), 0x0A(\n ), 0x0B(\v ), 0x0C(\f ), 0x0D(\r ), 0x20(space)
798+ """
799+ arr = np .array ([input_str ], dtype = 'U50' )
800+ result = arr .astype (QuadPrecDType ())
801+
802+ # Should successfully parse without errors
803+ result_val = str (result [0 ])
804+ assert result_val , f"Failed to parse with { description } "
805+
806+ # Verify the value is correct (strip whitespace and compare)
807+ stripped = input_str .strip (' \t \n \r \v \f ' )
808+ expected_arr = np .array ([stripped ], dtype = 'U50' )
809+ expected = expected_arr .astype (QuadPrecDType ())
810+
811+ if np .isnan (float (str (expected [0 ]))):
812+ assert np .isnan (float (str (result [0 ]))), f"NaN parsing failed for { description } "
813+ elif np .isinf (float (str (expected [0 ]))):
814+ assert np .isinf (float (str (result [0 ]))), f"Inf parsing failed for { description } "
815+ assert np .sign (float (str (expected [0 ]))) == np .sign (float (str (result [0 ]))), \
816+ f"Inf sign mismatch for { description } "
817+ else :
818+ assert result [0 ] == expected [0 ], f"Value mismatch for { description } "
819+
820+ @pytest .mark .parametrize ("invalid_str,description" , [
821+ ("1.23 abc" , "trailing non-whitespace after number" ),
822+ (" 1.23xyz " , "trailing garbage with surrounding whitespace" ),
823+ ("abc 123" , "leading garbage before number" ),
824+ ("1.23\x01 " , "control char (SOH) after number" ),
825+ ("1.23 a" , "letter after multiple spaces" ),
826+ ("\t 1.23\t abc\t " , "tabs with garbage in middle" ),
827+ ])
828+ def test_whitespace_with_invalid_trailing_content (self , invalid_str , description ):
829+ """Test that strings with invalid trailing content are rejected even with whitespace
830+
831+ This ensures the trailing whitespace check in casts.cpp properly validates
832+ that only whitespace follows the parsed number, not other characters.
833+ """
834+ arr = np .array ([invalid_str ], dtype = 'U50' )
835+
836+ with pytest .raises (ValueError , match = "could not convert string to QuadPrecision" ):
837+ arr .astype (QuadPrecDType ())
838+
839+ def test_empty_string_and_whitespace_only (self ):
840+ """Test that empty strings and whitespace-only strings raise errors"""
841+ test_cases = [
842+ "" , # Empty string
843+ " " , # Single space
844+ " " , # Multiple spaces
845+ "\t " , # Single tab
846+ "\n " , # Single newline
847+ "\r " , # Single carriage return
848+ "\v " , # Single vertical tab
849+ "\f " , # Single form feed
850+ " \t \n \r \v \f " , # All whitespace characters
851+ " \t \t \n \n " , # Mixed whitespace
852+ ]
853+
854+ for test_str in test_cases :
855+ arr = np .array ([test_str ], dtype = 'U20' )
856+ with pytest .raises (ValueError , match = "could not convert string to QuadPrecision" ):
857+ arr .astype (QuadPrecDType ())
858+
859+ @pytest .mark .parametrize ("boundary_str,description" , [
860+ ("1e4932" , "near max exponent for quad precision" ),
861+ ("1e-4932" , "near min exponent for quad precision" ),
862+ ("1.189731495357231765085759326628007016196477" + "e4932" , "very large number" ),
863+ ("3.362103143112093506262677817321752602596e-4932" , "very small number" ),
864+ ("-1.189731495357231765085759326628007016196477" + "e4932" , "very large negative" ),
865+ ("-3.362103143112093506262677817321752602596e-4932" , "very small negative" ),
866+ ])
867+ def test_extreme_exponent_values (self , boundary_str , description ):
868+ """Test parsing of numbers with extreme exponents near quad precision limits
869+
870+ IEEE 754 binary128 has exponent range of approximately ±4932
871+ """
872+ arr = np .array ([boundary_str ], dtype = 'U100' )
873+ result = arr .astype (QuadPrecDType ())
874+
875+ # Should parse successfully (may result in inf for overflow cases)
876+ result_str = str (result [0 ])
877+ assert result_str , f"Failed to parse { description } "
878+
879+ @pytest .mark .parametrize ("precision_str" , [
880+ "3.141592653589793238462643383279502884197" , # 36 digits (quad precision)
881+ "2.718281828459045235360287471352662497757" , # e to 36 digits
882+ "1.414213562373095048801688724209698078569" , # sqrt(2) to 36 digits
883+ "-1.732050807568877293527446341505872366942" , # -sqrt(3) to 36 digits
884+ ])
885+ def test_full_precision_parsing (self , precision_str ):
886+ """Test that strings with full quad precision (36 decimal digits) parse correctly
887+
888+ This ensures the full precision is preserved during string -> quad conversion
889+ """
890+ arr = np .array ([precision_str ], dtype = 'U50' )
891+ result = arr .astype (QuadPrecDType ())
892+
893+ # Convert back to string and verify roundtrip preserves precision
894+ back_to_str = result .astype ('U50' )
895+ roundtrip = back_to_str .astype (QuadPrecDType ())
896+
897+ # Roundtrip should preserve the value
898+ assert result [0 ] == roundtrip [0 ], \
899+ f"Precision lost in roundtrip for { precision_str } "
900+
776901
777902def test_basic_equality ():
778903 assert QuadPrecision ("12" ) == QuadPrecision (
0 commit comments