@@ -34,6 +34,7 @@ namespace arrow {
3434
3535using internal::AddWithOverflow;
3636using internal::DivideWithOverflow;
37+ using internal::ModuloWithOverflow;
3738using internal::MultiplyWithOverflow;
3839using internal::NegateWithOverflow;
3940using internal::SubtractWithOverflow;
@@ -468,6 +469,172 @@ struct FloatingDivideChecked {
468469 // TODO: Add decimal
469470};
470471
472+ // Remainder (truncated): result has same sign as dividend (C/C++ semantics)
473+ struct Remainder {
474+ template <typename T, typename Arg0, typename Arg1>
475+ static enable_if_floating_value<T> Call (KernelContext*, Arg0 left, Arg1 right,
476+ Status*) {
477+ return std::fmod (left, right);
478+ }
479+
480+ template <typename T, typename Arg0, typename Arg1>
481+ static enable_if_integer_value<T> Call (KernelContext*, Arg0 left, Arg1 right,
482+ Status* st) {
483+ T result;
484+ if (ARROW_PREDICT_FALSE (ModuloWithOverflow (left, right, &result))) {
485+ if (right == 0 ) {
486+ *st = Status::Invalid (" divide by zero" );
487+ } else {
488+ // INT_MIN % -1 overflow case, result is 0
489+ result = 0 ;
490+ }
491+ }
492+ return result;
493+ }
494+
495+ template <typename T, typename Arg0, typename Arg1>
496+ static enable_if_decimal_value<T> Call (KernelContext*, Arg0 left, Arg1 right,
497+ Status* st) {
498+ if (right == Arg1 ()) {
499+ *st = Status::Invalid (" divide by zero" );
500+ return T ();
501+ }
502+ return left % right;
503+ }
504+ };
505+
506+ struct RemainderChecked {
507+ template <typename T, typename Arg0, typename Arg1>
508+ static enable_if_floating_value<T> Call (KernelContext*, Arg0 left, Arg1 right,
509+ Status* st) {
510+ static_assert (std::is_same<T, Arg0>::value && std::is_same<T, Arg1>::value, " " );
511+ if (ARROW_PREDICT_FALSE (right == 0 )) {
512+ *st = Status::Invalid (" divide by zero" );
513+ return 0 ;
514+ }
515+ return std::fmod (left, right);
516+ }
517+
518+ template <typename T, typename Arg0, typename Arg1>
519+ static enable_if_integer_value<T> Call (KernelContext*, Arg0 left, Arg1 right,
520+ Status* st) {
521+ static_assert (std::is_same<T, Arg0>::value && std::is_same<T, Arg1>::value, " " );
522+ T result;
523+ if (ARROW_PREDICT_FALSE (ModuloWithOverflow (left, right, &result))) {
524+ if (right == 0 ) {
525+ *st = Status::Invalid (" divide by zero" );
526+ } else {
527+ *st = Status::Invalid (" overflow" );
528+ }
529+ }
530+ return result;
531+ }
532+
533+ template <typename T, typename Arg0, typename Arg1>
534+ static enable_if_decimal_value<T> Call (KernelContext* ctx, Arg0 left, Arg1 right,
535+ Status* st) {
536+ return Remainder::Call<T>(ctx, left, right, st);
537+ }
538+ };
539+
540+ // Helper: Convert truncated remainder to floored modulo for signed types.
541+ // Floored modulo has the same sign as the divisor (Python semantics).
542+ template <typename T>
543+ T AdjustRemainderToFloored (T rem, T right) {
544+ if constexpr (std::is_signed_v<T>) {
545+ if ((rem > 0 && right < 0 ) || (rem < 0 && right > 0 )) {
546+ rem += right;
547+ }
548+ }
549+ return rem;
550+ }
551+
552+ // Modulo (floored): result has same sign as divisor (Python semantics)
553+ struct Modulo {
554+ template <typename T, typename Arg0, typename Arg1>
555+ static enable_if_floating_value<T> Call (KernelContext*, Arg0 left, Arg1 right,
556+ Status*) {
557+ T rem = std::fmod (left, right);
558+ if (rem == 0 ) {
559+ // Preserve the sign based on divisor for zero results
560+ return std::copysign (rem, right);
561+ }
562+ return AdjustRemainderToFloored (rem, right);
563+ }
564+
565+ template <typename T, typename Arg0, typename Arg1>
566+ static enable_if_integer_value<T> Call (KernelContext*, Arg0 left, Arg1 right,
567+ Status* st) {
568+ T result;
569+ if (ARROW_PREDICT_FALSE (ModuloWithOverflow (left, right, &result))) {
570+ if (right == 0 ) {
571+ *st = Status::Invalid (" divide by zero" );
572+ } else {
573+ // INT_MIN % -1 overflow case, result is 0
574+ result = 0 ;
575+ }
576+ return result;
577+ }
578+ return AdjustRemainderToFloored (result, right);
579+ }
580+
581+ template <typename T, typename Arg0, typename Arg1>
582+ static enable_if_decimal_value<T> Call (KernelContext*, Arg0 left, Arg1 right,
583+ Status* st) {
584+ static const T kZero {};
585+ if (right == kZero ) {
586+ *st = Status::Invalid (" divide by zero" );
587+ return T ();
588+ }
589+ T rem = left % right;
590+ // Convert truncated to floored: adjust if signs differ
591+ if ((rem > kZero && right < kZero ) || (rem < kZero && right > kZero )) {
592+ rem = rem + right;
593+ }
594+ return rem;
595+ }
596+ };
597+
598+ struct ModuloChecked {
599+ template <typename T, typename Arg0, typename Arg1>
600+ static enable_if_floating_value<T> Call (KernelContext*, Arg0 left, Arg1 right,
601+ Status* st) {
602+ static_assert (std::is_same<T, Arg0>::value && std::is_same<T, Arg1>::value, " " );
603+ if (ARROW_PREDICT_FALSE (right == 0 )) {
604+ *st = Status::Invalid (" divide by zero" );
605+ return 0 ;
606+ }
607+ T rem = std::fmod (left, right);
608+ if (rem == 0 ) {
609+ // Preserve the sign based on divisor for zero results
610+ return std::copysign (rem, right);
611+ }
612+ return AdjustRemainderToFloored (rem, right);
613+ }
614+
615+ template <typename T, typename Arg0, typename Arg1>
616+ static enable_if_integer_value<T> Call (KernelContext*, Arg0 left, Arg1 right,
617+ Status* st) {
618+ static_assert (std::is_same<T, Arg0>::value && std::is_same<T, Arg1>::value, " " );
619+ T result;
620+ if (ARROW_PREDICT_FALSE (ModuloWithOverflow (left, right, &result))) {
621+ if (right == 0 ) {
622+ *st = Status::Invalid (" divide by zero" );
623+ } else {
624+ *st = Status::Invalid (" overflow" );
625+ }
626+ return result;
627+ }
628+ return AdjustRemainderToFloored (result, right);
629+ }
630+
631+ template <typename T, typename Arg0, typename Arg1>
632+ static enable_if_decimal_value<T> Call (KernelContext* ctx, Arg0 left, Arg1 right,
633+ Status* st) {
634+ return Modulo::Call<T>(ctx, left, right, st);
635+ }
636+ };
637+
471638struct Negate {
472639 template <typename T, typename Arg>
473640 static constexpr enable_if_floating_value<T> Call (KernelContext*, Arg arg, Status*) {
0 commit comments