@@ -408,6 +408,165 @@ create_quad_unary_2out_ufunc(PyObject *numpy, const char *ufunc_name)
408408 return 0 ;
409409}
410410
411+ // Frexp-specific resolver: QuadPrecDType -> (QuadPrecDType, int32)
412+ static NPY_CASTING
413+ quad_frexp_resolve_descriptors (PyObject *self, PyArray_DTypeMeta *const dtypes[],
414+ PyArray_Descr *const given_descrs[], PyArray_Descr *loop_descrs[],
415+ npy_intp *NPY_UNUSED (view_offset))
416+ {
417+ // Input descriptor
418+ Py_INCREF (given_descrs[0 ]);
419+ loop_descrs[0 ] = given_descrs[0 ];
420+
421+ // Output 1: mantissa (same dtype as input)
422+ if (given_descrs[1 ] == NULL ) {
423+ Py_INCREF (given_descrs[0 ]);
424+ loop_descrs[1 ] = given_descrs[0 ];
425+ }
426+ else {
427+ Py_INCREF (given_descrs[1 ]);
428+ loop_descrs[1 ] = given_descrs[1 ];
429+ }
430+
431+ // Output 2: exponent (int32)
432+ if (given_descrs[2 ] == NULL ) {
433+ loop_descrs[2 ] = PyArray_DescrFromType (NPY_INT32);
434+ }
435+ else {
436+ Py_INCREF (given_descrs[2 ]);
437+ loop_descrs[2 ] = given_descrs[2 ];
438+ }
439+
440+ QuadPrecDTypeObject *descr_in = (QuadPrecDTypeObject *)given_descrs[0 ];
441+ QuadPrecDTypeObject *descr_out1 = (QuadPrecDTypeObject *)loop_descrs[1 ];
442+
443+ if (descr_in->backend != descr_out1->backend ) {
444+ return NPY_UNSAFE_CASTING;
445+ }
446+
447+ return NPY_NO_CASTING;
448+ }
449+
450+ // Strided loop for frexp (unaligned)
451+ template <frexp_op_quad_def sleef_op, frexp_op_longdouble_def longdouble_op>
452+ int
453+ quad_frexp_strided_loop_unaligned (PyArrayMethod_Context *context, char *const data[],
454+ npy_intp const dimensions[], npy_intp const strides[],
455+ NpyAuxData *auxdata)
456+ {
457+ npy_intp N = dimensions[0 ];
458+ char *in_ptr = data[0 ];
459+ char *out_mantissa_ptr = data[1 ];
460+ char *out_exp_ptr = data[2 ];
461+ npy_intp in_stride = strides[0 ];
462+ npy_intp out_mantissa_stride = strides[1 ];
463+ npy_intp out_exp_stride = strides[2 ];
464+
465+ QuadPrecDTypeObject *descr = (QuadPrecDTypeObject *)context->descriptors [0 ];
466+ QuadBackendType backend = descr->backend ;
467+ size_t elem_size = (backend == BACKEND_SLEEF) ? sizeof (Sleef_quad) : sizeof (long double );
468+
469+ quad_value in, out_mantissa;
470+ int out_exp;
471+
472+ while (N--) {
473+ memcpy (&in, in_ptr, elem_size);
474+ if (backend == BACKEND_SLEEF) {
475+ out_mantissa.sleef_value = sleef_op (&in.sleef_value , &out_exp);
476+ }
477+ else {
478+ out_mantissa.longdouble_value = longdouble_op (&in.longdouble_value , &out_exp);
479+ }
480+ memcpy (out_mantissa_ptr, &out_mantissa, elem_size);
481+ memcpy (out_exp_ptr, &out_exp, sizeof (int ));
482+
483+ in_ptr += in_stride;
484+ out_mantissa_ptr += out_mantissa_stride;
485+ out_exp_ptr += out_exp_stride;
486+ }
487+ return 0 ;
488+ }
489+
490+ // Strided loop for frexp (aligned)
491+ template <frexp_op_quad_def sleef_op, frexp_op_longdouble_def longdouble_op>
492+ int
493+ quad_frexp_strided_loop_aligned (PyArrayMethod_Context *context, char *const data[],
494+ npy_intp const dimensions[], npy_intp const strides[],
495+ NpyAuxData *auxdata)
496+ {
497+ npy_intp N = dimensions[0 ];
498+ char *in_ptr = data[0 ];
499+ char *out_mantissa_ptr = data[1 ];
500+ char *out_exp_ptr = data[2 ];
501+ npy_intp in_stride = strides[0 ];
502+ npy_intp out_mantissa_stride = strides[1 ];
503+ npy_intp out_exp_stride = strides[2 ];
504+
505+ QuadPrecDTypeObject *descr = (QuadPrecDTypeObject *)context->descriptors [0 ];
506+ QuadBackendType backend = descr->backend ;
507+
508+ int out_exp;
509+
510+ while (N--) {
511+ if (backend == BACKEND_SLEEF) {
512+ Sleef_quad mantissa = sleef_op ((Sleef_quad *)in_ptr, &out_exp);
513+ memcpy (out_mantissa_ptr, &mantissa, sizeof (Sleef_quad));
514+ }
515+ else {
516+ long double mantissa = longdouble_op ((long double *)in_ptr, &out_exp);
517+ memcpy (out_mantissa_ptr, &mantissa, sizeof (long double ));
518+ }
519+ memcpy (out_exp_ptr, &out_exp, sizeof (int ));
520+
521+ in_ptr += in_stride;
522+ out_mantissa_ptr += out_mantissa_stride;
523+ out_exp_ptr += out_exp_stride;
524+ }
525+ return 0 ;
526+ }
527+
528+
529+ template <frexp_op_quad_def sleef_op, frexp_op_longdouble_def longdouble_op>
530+ int
531+ create_quad_frexp_ufunc (PyObject *numpy, const char *ufunc_name)
532+ {
533+ PyObject *ufunc = PyObject_GetAttrString (numpy, ufunc_name);
534+ if (ufunc == NULL ) {
535+ return -1 ;
536+ }
537+
538+ // 1 input (QuadPrecDType), 2 outputs (QuadPrecDType, int32)
539+ PyArray_DTypeMeta *dtypes[3 ] = {
540+ &QuadPrecDType,
541+ &QuadPrecDType,
542+ &PyArray_Int32DType
543+ };
544+
545+ PyType_Slot slots[] = {
546+ {NPY_METH_resolve_descriptors, (void *)&quad_frexp_resolve_descriptors},
547+ {NPY_METH_strided_loop,
548+ (void *)&quad_frexp_strided_loop_aligned<sleef_op, longdouble_op>},
549+ {NPY_METH_unaligned_strided_loop,
550+ (void *)&quad_frexp_strided_loop_unaligned<sleef_op, longdouble_op>},
551+ {0 , NULL }};
552+
553+ PyArrayMethod_Spec Spec = {
554+ .name = " quad_frexp" ,
555+ .nin = 1 ,
556+ .nout = 2 ,
557+ .casting = NPY_NO_CASTING,
558+ .flags = NPY_METH_SUPPORTS_UNALIGNED,
559+ .dtypes = dtypes,
560+ .slots = slots,
561+ };
562+
563+ if (PyUFunc_AddLoopFromSpec (ufunc, &Spec) < 0 ) {
564+ return -1 ;
565+ }
566+
567+ return 0 ;
568+ }
569+
411570int
412571init_quad_unary_ops (PyObject *numpy)
413572{
@@ -540,5 +699,10 @@ init_quad_unary_ops(PyObject *numpy)
540699 return -1 ;
541700 }
542701
702+ // Frexp: special case with (QuadPrecDType, int32) outputs
703+ if (create_quad_frexp_ufunc<quad_frexp, ld_frexp>(numpy, " frexp" ) < 0 ) {
704+ return -1 ;
705+ }
706+
543707 return 0 ;
544708}
0 commit comments