@@ -62,6 +62,105 @@ ascii_strncasecmp(const char *s1, const char *s2, size_t n)
6262 return 0 ;
6363}
6464
65+ /*
66+ * Internal helper: Parse numeric string to quad-precision.
67+ * Assumes no leading whitespace and no inf/nan (caller handles those).
68+ * The 'start' parameter points to where the original string started (for endptr on error).
69+ */
70+ static int
71+ cstring_to_quad_internal (const char * str , const char * start , QuadBackendType backend ,
72+ quad_value * out_value , char * * endptr )
73+ {
74+ if (backend == BACKEND_SLEEF ) {
75+ // SLEEF 4.0's Sleef_strtoq doesn't properly set endptr to indicate
76+ // where parsing stopped. We need to manually validate and track the parse position.
77+
78+ const char * p = str ;
79+
80+ // Handle optional sign
81+ if (* p == '+' || * p == '-' ) {
82+ p ++ ;
83+ }
84+
85+ // Must have at least one digit or decimal point followed by digit
86+ int has_digits = 0 ;
87+
88+ // Parse integer part
89+ while (ascii_isdigit (* p )) {
90+ has_digits = 1 ;
91+ p ++ ;
92+ }
93+
94+ // Parse decimal point and fractional part
95+ if (* p == '.' ) {
96+ p ++ ;
97+ while (ascii_isdigit (* p )) {
98+ has_digits = 1 ;
99+ p ++ ;
100+ }
101+ }
102+
103+ // Must have at least one digit somewhere
104+ if (!has_digits ) {
105+ if (endptr ) * endptr = (char * )start ;
106+ return -1 ;
107+ }
108+
109+ // Parse optional exponent
110+ if (* p == 'e' || * p == 'E' ) {
111+ const char * exp_start = p ;
112+ p ++ ;
113+
114+ // Optional sign in exponent
115+ if (* p == '+' || * p == '-' ) {
116+ p ++ ;
117+ }
118+
119+ // Must have at least one digit in exponent
120+ if (!ascii_isdigit (* p )) {
121+ // Invalid exponent, backtrack
122+ p = exp_start ;
123+ } else {
124+ while (ascii_isdigit (* p )) {
125+ p ++ ;
126+ }
127+ }
128+ }
129+
130+ // Now p points to where valid parsing ends
131+ // Create a null-terminated substring for SLEEF
132+ size_t len = p - str ;
133+ char * temp = (char * )malloc (len + 1 );
134+ if (!temp ) {
135+ if (endptr ) * endptr = (char * )start ;
136+ return -1 ;
137+ }
138+ memcpy (temp , str , len );
139+ temp [len ] = '\0' ;
140+
141+ // Call Sleef_strtoq with the bounded string
142+ char * sleef_endptr ;
143+ out_value -> sleef_value = Sleef_strtoq (temp , & sleef_endptr );
144+ free (temp );
145+
146+ // Set endptr to our calculated position
147+ if (endptr ) {
148+ * endptr = (char * )p ;
149+ }
150+
151+ } else {
152+ out_value -> longdouble_value = strtold (str , endptr );
153+ }
154+
155+ if (endptr && * endptr == str ) {
156+ // Nothing was parsed - set endptr to original start
157+ * endptr = (char * )start ;
158+ return -1 ;
159+ }
160+
161+ return 0 ; // success
162+ }
163+
65164/*
66165 * NumPyOS_ascii_strtoq:
67166 *
@@ -71,7 +170,7 @@ ascii_strncasecmp(const char *s1, const char *s2, size_t n)
71170 * This function:
72171 * - Skips leading whitespace
73172 * - Recognizes inf/nan case-insensitively with optional signs and payloads
74- * - Delegates to cstring_to_quad for numeric parsing
173+ * - Parses numeric values
75174 *
76175 * Returns:
77176 * 0 on success
@@ -153,110 +252,9 @@ NumPyOS_ascii_strtoq(const char *s, QuadBackendType backend, quad_value *out_val
153252 return 0 ;
154253 }
155254
156- // For numeric values, delegate to cstring_to_quad
157- // Pass the original string position (after whitespace, includes sign if present)
158- return cstring_to_quad (s , backend , out_value , endptr , false);
159- }
160-
161- int cstring_to_quad (const char * str , QuadBackendType backend , quad_value * out_value ,
162- char * * endptr , bool require_full_parse )
163- {
164- if (backend == BACKEND_SLEEF ) {
165- // SLEEF 4.0's Sleef_strtoq doesn't properly set endptr to indicate
166- // where parsing stopped. It always sets endptr to the end of the string.
167- // We need to manually validate and track the parse position.
168-
169- const char * p = str ;
170-
171- // Skip leading whitespace
172- while (ascii_isspace (* p )) {
173- p ++ ;
174- }
175-
176- // Handle optional sign
177- if (* p == '+' || * p == '-' ) {
178- p ++ ;
179- }
180-
181- // Must have at least one digit or decimal point followed by digit
182- int has_digits = 0 ;
183-
184- // Parse integer part
185- while (ascii_isdigit (* p )) {
186- has_digits = 1 ;
187- p ++ ;
188- }
189-
190- // Parse decimal point and fractional part
191- if (* p == '.' ) {
192- p ++ ;
193- while (ascii_isdigit (* p )) {
194- has_digits = 1 ;
195- p ++ ;
196- }
197- }
198-
199- // Must have at least one digit somewhere
200- if (!has_digits ) {
201- if (endptr ) * endptr = (char * )str ;
202- return -1 ;
203- }
204-
205- // Parse optional exponent
206- if (* p == 'e' || * p == 'E' ) {
207- const char * exp_start = p ;
208- p ++ ;
209-
210- // Optional sign in exponent
211- if (* p == '+' || * p == '-' ) {
212- p ++ ;
213- }
214-
215- // Must have at least one digit in exponent
216- if (!ascii_isdigit (* p )) {
217- // Invalid exponent, backtrack
218- p = exp_start ;
219- } else {
220- while (ascii_isdigit (* p )) {
221- p ++ ;
222- }
223- }
224- }
225-
226- // Now p points to where valid parsing ends
227- // SLEEF 4.0's Sleef_strtoq has a bug where it doesn't properly stop at whitespace
228- // or other delimiters. We need to create a null-terminated substring.
229- size_t len = p - str ;
230- char * temp = (char * )malloc (len + 1 );
231- if (!temp ) {
232- if (endptr ) * endptr = (char * )str ;
233- return -1 ;
234- }
235- memcpy (temp , str , len );
236- temp [len ] = '\0' ;
237-
238- // Call Sleef_strtoq with the bounded string
239- char * sleef_endptr ;
240- out_value -> sleef_value = Sleef_strtoq (temp , & sleef_endptr );
241- free (temp );
242-
243- // Set endptr to our calculated position
244- if (endptr ) {
245- * endptr = (char * )p ;
246- }
247-
248- } else {
249- out_value -> longdouble_value = strtold (str , endptr );
250- }
251-
252- if (endptr && * endptr == str )
253- return -1 ; // parse error - nothing was parsed
254-
255- // If full parse is required
256- if (require_full_parse && endptr && * * endptr != '\0' )
257- return -1 ; // parse error - characters remain to be converted
258-
259- return 0 ; // success
255+ // For numeric values, parse starting from 's' (includes sign if present)
256+ // The sign is part of the number, not handled separately like inf/nan
257+ return cstring_to_quad_internal (s , s , backend , out_value , endptr );
260258}
261259
262260// Helper function: Convert quad_value to Sleef_quad for Dragon4
0 commit comments