@@ -134,12 +134,17 @@ NumPyOS_ascii_strtoq(const char *s, QuadBackendType backend, quad_value *out_val
134134 }
135135 }
136136
137- // Set NaN value ( sign is ignored for NaN)
137+ // Set NaN value with sign preserved
138138 if (backend == BACKEND_SLEEF ) {
139- out_value -> sleef_value = QUAD_PRECISION_NAN ;
139+ Sleef_quad nan_val = QUAD_PRECISION_NAN ;
140+ // Apply sign to NaN (negative NaN has sign bit set)
141+ if (sign < 0 ) {
142+ nan_val = Sleef_negq1 (nan_val );
143+ }
144+ out_value -> sleef_value = nan_val ;
140145 }
141146 else {
142- out_value -> longdouble_value = nanl ("" );
147+ out_value -> longdouble_value = sign < 0 ? - nanl ( "" ) : nanl ("" );
143148 }
144149
145150 if (endptr ) {
@@ -157,15 +162,103 @@ int cstring_to_quad(const char *str, QuadBackendType backend, quad_value *out_va
157162char * * endptr , bool require_full_parse )
158163{
159164 if (backend == BACKEND_SLEEF ) {
160- out_value -> sleef_value = Sleef_strtoq (str , endptr );
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+ // Track start of number (after whitespace)
177+ const char * num_start = p ;
178+
179+ // Handle optional sign
180+ if (* p == '+' || * p == '-' ) {
181+ p ++ ;
182+ }
183+
184+ // Must have at least one digit or decimal point followed by digit
185+ int has_digits = 0 ;
186+ int has_decimal = 0 ;
187+
188+ // Parse integer part
189+ while (ascii_isdigit (* p )) {
190+ has_digits = 1 ;
191+ p ++ ;
192+ }
193+
194+ // Parse decimal point and fractional part
195+ if (* p == '.' ) {
196+ has_decimal = 1 ;
197+ p ++ ;
198+ while (ascii_isdigit (* p )) {
199+ has_digits = 1 ;
200+ p ++ ;
201+ }
202+ }
203+
204+ // Must have at least one digit somewhere
205+ if (!has_digits ) {
206+ if (endptr ) * endptr = (char * )str ;
207+ return -1 ;
208+ }
209+
210+ // Parse optional exponent
211+ if (* p == 'e' || * p == 'E' ) {
212+ const char * exp_start = p ;
213+ p ++ ;
214+
215+ // Optional sign in exponent
216+ if (* p == '+' || * p == '-' ) {
217+ p ++ ;
218+ }
219+
220+ // Must have at least one digit in exponent
221+ if (!ascii_isdigit (* p )) {
222+ // Invalid exponent, backtrack
223+ p = exp_start ;
224+ } else {
225+ while (ascii_isdigit (* p )) {
226+ p ++ ;
227+ }
228+ }
229+ }
230+
231+ // Now p points to where valid parsing ends
232+ // SLEEF 4.0's Sleef_strtoq has a bug where it doesn't properly stop at whitespace
233+ // or other delimiters. We need to create a null-terminated substring.
234+ size_t len = p - str ;
235+ char * temp = (char * )malloc (len + 1 );
236+ if (!temp ) {
237+ if (endptr ) * endptr = (char * )str ;
238+ return -1 ;
239+ }
240+ memcpy (temp , str , len );
241+ temp [len ] = '\0' ;
242+
243+ // Call Sleef_strtoq with the bounded string
244+ char * sleef_endptr ;
245+ out_value -> sleef_value = Sleef_strtoq (temp , & sleef_endptr );
246+ free (temp );
247+
248+ // Set endptr to our calculated position
249+ if (endptr ) {
250+ * endptr = (char * )p ;
251+ }
252+
161253 } else {
162254 out_value -> longdouble_value = strtold (str , endptr );
163255 }
164- if (* endptr == str )
256+
257+ if (endptr && * endptr == str )
165258 return -1 ; // parse error - nothing was parsed
166259
167260 // If full parse is required
168- if (require_full_parse && * * endptr != '\0' )
261+ if (require_full_parse && endptr && * * endptr != '\0' )
169262 return -1 ; // parse error - characters remain to be converted
170263
171264 return 0 ; // success
0 commit comments