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{
@@ -443,8 +449,7 @@ QuadPrecision_is_integer(QuadPrecisionObject *self, PyObject *Py_UNUSED(ignored)
443449 value = self -> value .sleef_value ;
444450 }
445451 else {
446- // lets also tackle ld from sleef functions as well
447- value = Sleef_cast_from_doubleq1 ((double )self -> value .longdouble_value );
452+ value = longdouble_to_quad (self -> value .longdouble_value );
448453 }
449454
450455 if (Sleef_iunordq1 (value , value )) {
@@ -462,9 +467,7 @@ QuadPrecision_is_integer(QuadPrecisionObject *self, PyObject *Py_UNUSED(ignored)
462467
463468 // Check if value equals its truncated version
464469 Sleef_quad truncated = Sleef_truncq1 (value );
465- int32_t is_equal = Sleef_icmpeqq1 (value , truncated );
466-
467- if (is_equal ) {
470+ if (Sleef_icmpeqq1 (value , truncated )) {
468471 Py_RETURN_TRUE ;
469472 }
470473 else {
@@ -474,7 +477,7 @@ QuadPrecision_is_integer(QuadPrecisionObject *self, PyObject *Py_UNUSED(ignored)
474477
475478PyObject * quad_to_pylong (Sleef_quad value )
476479{
477- char buffer [128 ];
480+ char buffer [4936 ]; // 4933 + sign + null terminator, enough for 128-bit integer in decimal
478481
479482 // Sleef_snprintf call is thread-unsafe
480483 LOCK_SLEEF ;
@@ -492,6 +495,37 @@ PyObject* quad_to_pylong(Sleef_quad value)
492495 return result ;
493496}
494497
498+ PyObject * longdouble_to_pylong (long double value )
499+ {
500+ char buffer [4936 ]; // 4933 + sign + null terminator, enough for 128-bit integer in decimal
501+
502+ // POSIX guarantees thread-safety of snprintf
503+ int written = snprintf (buffer , sizeof (buffer ), "%.0Lf" , value );
504+ if (written < 0 || written >= sizeof (buffer )) {
505+ PyErr_SetString (PyExc_RuntimeError , "Failed to convert long double to string" );
506+ return NULL ;
507+ }
508+
509+ // Already raises ValueError and returns NULL on failure
510+ return PyLong_FromString (buffer , NULL , 10 );
511+ }
512+
513+ static Sleef_quad
514+ longdouble_to_quad (long double value )
515+ {
516+ if (isnan (value ) || isinf (value ) || value == 0.0L )
517+ return Sleef_cast_from_doubleq1 ((double )value );
518+
519+ int exp ;
520+ long double mantissa = frexpl (value , & exp );
521+ long double scaled = ldexpl (mantissa , 64 );
522+ exp -= 64 ;
523+ Sleef_quad q = (scaled < 0 )
524+ ? Sleef_negq1 (Sleef_cast_from_uint64q1 ((uint64_t )(- scaled )))
525+ : Sleef_cast_from_uint64q1 ((uint64_t )scaled );
526+ return Sleef_ldexpq1 (q , exp );
527+ }
528+
495529// inspired by the CPython implementation
496530// https://github.com/python/cpython/blob/ac1ffd77858b62d169a08040c08aa5de26e145ac/Objects/floatobject.c#L1503C1-L1572C2
497531static PyObject *
@@ -504,10 +538,8 @@ QuadPrecision_as_integer_ratio(QuadPrecisionObject *self, PyObject *Py_UNUSED(ig
504538
505539 if (self -> backend == BACKEND_SLEEF ) {
506540 value = self -> value .sleef_value ;
507- }
508- else {
509- // lets also tackle ld from sleef functions as well
510- value = Sleef_cast_from_doubleq1 ((double )self -> value .longdouble_value );
541+ } else {
542+ value = longdouble_to_quad (self -> value .longdouble_value );
511543 }
512544
513545 if (Sleef_iunordq1 (value , value )) {
@@ -653,7 +685,7 @@ QuadPrecision_hash(QuadPrecisionObject *self)
653685 value = self -> value .sleef_value ;
654686 }
655687 else {
656- value = Sleef_cast_from_doubleq1 (( double ) self -> value .longdouble_value );
688+ value = longdouble_to_quad ( self -> value .longdouble_value );
657689 }
658690
659691 // Check for NaN - use pointer hash (each NaN instance gets unique hash)
0 commit comments