22#include <sleef.h>
33#include <sleefquad.h>
44#include <stdlib.h>
5+ #include <math.h>
56
67#define PY_ARRAY_UNIQUE_SYMBOL QuadPrecType_ARRAY_API
78#define NPY_NO_DEPRECATED_API NPY_2_0_API_VERSION
2122#include "pythoncapi_compat.h"
2223
2324
25+ // forward declaration
26+ static Sleef_quad
27+ longdouble_to_quad (long double value );
28+
29+
2430QuadPrecisionObject *
2531QuadPrecision_raw_new (QuadBackendType backend )
2632{
@@ -430,8 +436,7 @@ QuadPrecision_is_integer(QuadPrecisionObject *self, PyObject *Py_UNUSED(ignored)
430436 value = self -> value .sleef_value ;
431437 }
432438 else {
433- // lets also tackle ld from sleef functions as well
434- value = Sleef_cast_from_doubleq1 ((double )self -> value .longdouble_value );
439+ value = longdouble_to_quad (self -> value .longdouble_value );
435440 }
436441
437442 if (Sleef_iunordq1 (value , value )) {
@@ -449,9 +454,7 @@ QuadPrecision_is_integer(QuadPrecisionObject *self, PyObject *Py_UNUSED(ignored)
449454
450455 // Check if value equals its truncated version
451456 Sleef_quad truncated = Sleef_truncq1 (value );
452- int32_t is_equal = Sleef_icmpeqq1 (value , truncated );
453-
454- if (is_equal ) {
457+ if (Sleef_icmpeqq1 (value , truncated )) {
455458 Py_RETURN_TRUE ;
456459 }
457460 else {
@@ -461,7 +464,7 @@ QuadPrecision_is_integer(QuadPrecisionObject *self, PyObject *Py_UNUSED(ignored)
461464
462465PyObject * quad_to_pylong (Sleef_quad value )
463466{
464- char buffer [128 ];
467+ char buffer [4936 ]; // 4933 + sign + null terminator, enough for 128-bit integer in decimal
465468
466469 // Sleef_snprintf call is thread-unsafe
467470 LOCK_SLEEF ;
@@ -479,6 +482,37 @@ PyObject* quad_to_pylong(Sleef_quad value)
479482 return result ;
480483}
481484
485+ PyObject * longdouble_to_pylong (long double value )
486+ {
487+ char buffer [4936 ]; // 4933 + sign + null terminator, enough for 128-bit integer in decimal
488+
489+ // POSIX guarantees thread-safety of snprintf
490+ int written = snprintf (buffer , sizeof (buffer ), "%.0Lf" , value );
491+ if (written < 0 || written >= sizeof (buffer )) {
492+ PyErr_SetString (PyExc_RuntimeError , "Failed to convert long double to string" );
493+ return NULL ;
494+ }
495+
496+ // Already raises ValueError and returns NULL on failure
497+ return PyLong_FromString (buffer , NULL , 10 );
498+ }
499+
500+ static Sleef_quad
501+ longdouble_to_quad (long double value )
502+ {
503+ if (isnan (value ) || isinf (value ) || value == 0.0L )
504+ return Sleef_cast_from_doubleq1 ((double )value );
505+
506+ int exp ;
507+ long double mantissa = frexpl (value , & exp );
508+ long double scaled = ldexpl (mantissa , 64 );
509+ exp -= 64 ;
510+ Sleef_quad q = (scaled < 0 )
511+ ? Sleef_negq1 (Sleef_cast_from_uint64q1 ((uint64_t )(- scaled )))
512+ : Sleef_cast_from_uint64q1 ((uint64_t )scaled );
513+ return Sleef_ldexpq1 (q , exp );
514+ }
515+
482516// inspired by the CPython implementation
483517// https://github.com/python/cpython/blob/ac1ffd77858b62d169a08040c08aa5de26e145ac/Objects/floatobject.c#L1503C1-L1572C2
484518static PyObject *
@@ -491,10 +525,8 @@ QuadPrecision_as_integer_ratio(QuadPrecisionObject *self, PyObject *Py_UNUSED(ig
491525
492526 if (self -> backend == BACKEND_SLEEF ) {
493527 value = self -> value .sleef_value ;
494- }
495- else {
496- // lets also tackle ld from sleef functions as well
497- value = Sleef_cast_from_doubleq1 ((double )self -> value .longdouble_value );
528+ } else {
529+ value = longdouble_to_quad (self -> value .longdouble_value );
498530 }
499531
500532 if (Sleef_iunordq1 (value , value )) {
@@ -697,7 +729,7 @@ QuadPrecision_hash(QuadPrecisionObject *self)
697729 value = self -> value .sleef_value ;
698730 }
699731 else {
700- value = Sleef_cast_from_doubleq1 (( double ) self -> value .longdouble_value );
732+ value = longdouble_to_quad ( self -> value .longdouble_value );
701733 }
702734
703735 // Check for NaN - use pointer hash (each NaN instance gets unique hash)
0 commit comments