]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
PR 49010,24518 MOD/MODULO fixes.
authorJanne Blomqvist <jb@gcc.gnu.org>
Sat, 5 May 2012 07:59:22 +0000 (10:59 +0300)
committerJanne Blomqvist <jb@gcc.gnu.org>
Sat, 5 May 2012 07:59:22 +0000 (10:59 +0300)
gcc/fortran:

2012-05-05  Janne Blomqvist  <jb@gcc.gnu.org>

PR fortran/49010
PR fortran/24518
* intrinsic.texi (MOD, MODULO): Mention sign and magnitude of result.
* simplify.c (gfc_simplify_mod): Use mpfr_fmod.
(gfc_simplify_modulo): Likewise, use copysign to fix the result if
zero.
* trans-intrinsic.c (gfc_conv_intrinsic_mod): Remove fallback as
builtin_fmod is always available. For modulo, call copysign to fix
the result when signed zeros are enabled.

testsuite:

2012-05-05  Janne Blomqvist  <jb@gcc.gnu.org>

PR fortran/49010
PR fortran/24518
* gfortran.dg/mod_sign0_1.f90: New test.
* gfortran.dg/mod_large_1.f90: New test.

From-SVN: r187191

gcc/fortran/ChangeLog
gcc/fortran/intrinsic.texi
gcc/fortran/simplify.c
gcc/fortran/trans-intrinsic.c
gcc/testsuite/ChangeLog
gcc/testsuite/gfortran.dg/mod_large_1.f90 [new file with mode: 0644]
gcc/testsuite/gfortran.dg/mod_sign0_1.f90 [new file with mode: 0644]

index d1cb4294be6f8bfd321b3681271dc224923af6bd..a9b4195499dc0f2999c324b2fa1c138f1c252602 100644 (file)
@@ -1,3 +1,15 @@
+2012-05-05  Janne Blomqvist  <jb@gcc.gnu.org>
+
+       PR fortran/49010
+       PR fortran/24518
+       * intrinsic.texi (MOD, MODULO): Mention sign and magnitude of result.
+       * simplify.c (gfc_simplify_mod): Use mpfr_fmod.
+       (gfc_simplify_modulo): Likewise, use copysign to fix the result if
+       zero.
+       * trans-intrinsic.c (gfc_conv_intrinsic_mod): Remove fallback as
+       builtin_fmod is always available. For modulo, call copysign to fix
+       the result when signed zeros are enabled.
+
 2012-05-05  Janne Blomqvist  <jb@gcc.gnu.org>
 
         * gfortran.texi (GFORTRAN_TMPDIR): Rename to TMPDIR, explain
index 294818e43d0a1c62d4d5250ec56605cb1b29202b..9bc36d7d4153f412dd65f795beee26e6411da6c0 100644 (file)
@@ -8991,8 +8991,7 @@ cases, the result is of the same type and kind as @var{ARRAY}.
 
 @table @asis
 @item @emph{Description}:
-@code{MOD(A,P)} computes the remainder of the division of A by P@. It is
-calculated as @code{A - (INT(A/P) * P)}.
+@code{MOD(A,P)} computes the remainder of the division of A by P@. 
 
 @item @emph{Standard}:
 Fortran 77 and later
@@ -9005,14 +9004,16 @@ Elemental function
 
 @item @emph{Arguments}:
 @multitable @columnfractions .15 .70
-@item @var{A} @tab Shall be a scalar of type @code{INTEGER} or @code{REAL}
-@item @var{P} @tab Shall be a scalar of the same type as @var{A} and not
-equal to zero
+@item @var{A} @tab Shall be a scalar of type @code{INTEGER} or @code{REAL}.
+@item @var{P} @tab Shall be a scalar of the same type and kind as @var{A} 
+and not equal to zero.
 @end multitable
 
 @item @emph{Return value}:
-The kind of the return value is the result of cross-promoting
-the kinds of the arguments.
+The return value is the result of @code{A - (INT(A/P) * P)}. The type
+and kind of the return value is the same as that of the arguments. The
+returned value has the same sign as A and a magnitude less than the
+magnitude of P.
 
 @item @emph{Example}:
 @smallexample
@@ -9041,6 +9042,10 @@ end program test_mod
 @item @code{AMOD(A,P)} @tab @code{REAL(4) A,P} @tab @code{REAL(4)} @tab Fortran 95 and later
 @item @code{DMOD(A,P)} @tab @code{REAL(8) A,P} @tab @code{REAL(8)} @tab Fortran 95 and later
 @end multitable
+
+@item @emph{See also}:
+@ref{MODULO}
+
 @end table
 
 
@@ -9066,8 +9071,9 @@ Elemental function
 
 @item @emph{Arguments}:
 @multitable @columnfractions .15 .70
-@item @var{A} @tab Shall be a scalar of type @code{INTEGER} or @code{REAL}
-@item @var{P} @tab Shall be a scalar of the same type and kind as @var{A}
+@item @var{A} @tab Shall be a scalar of type @code{INTEGER} or @code{REAL}.
+@item @var{P} @tab Shall be a scalar of the same type and kind as @var{A}. 
+It shall not be zero.
 @end multitable
 
 @item @emph{Return value}:
@@ -9080,7 +9086,8 @@ The type and kind of the result are those of the arguments.
 @item If @var{A} and @var{P} are of type @code{REAL}:
 @code{MODULO(A,P)} has the value of @code{A - FLOOR (A / P) * P}.
 @end table
-In all cases, if @var{P} is zero the result is processor-dependent.
+The returned value has the same sign as P and a magnitude less than
+the magnitude of P.
 
 @item @emph{Example}:
 @smallexample
@@ -9096,6 +9103,9 @@ program test_modulo
 end program
 @end smallexample
 
+@item @emph{See also}:
+@ref{MOD}
+
 @end table
 
 
index 706dab440ce870569071e7ffa578d64b6b7862f4..1578db19b94866079bc2edc9aad26f23e00330b4 100644 (file)
@@ -4222,7 +4222,6 @@ gfc_expr *
 gfc_simplify_mod (gfc_expr *a, gfc_expr *p)
 {
   gfc_expr *result;
-  mpfr_t tmp;
   int kind;
 
   if (a->expr_type != EXPR_CONSTANT || p->expr_type != EXPR_CONSTANT)
@@ -4254,12 +4253,8 @@ gfc_simplify_mod (gfc_expr *a, gfc_expr *p)
          }
 
        gfc_set_model_kind (kind);
-       mpfr_init (tmp);
-       mpfr_div (tmp, a->value.real, p->value.real, GFC_RND_MODE);
-       mpfr_trunc (tmp, tmp);
-       mpfr_mul (tmp, tmp, p->value.real, GFC_RND_MODE);
-       mpfr_sub (result->value.real, a->value.real, tmp, GFC_RND_MODE);
-       mpfr_clear (tmp);
+       mpfr_fmod (result->value.real, a->value.real, p->value.real, 
+                  GFC_RND_MODE);
        break;
 
       default:
@@ -4274,7 +4269,6 @@ gfc_expr *
 gfc_simplify_modulo (gfc_expr *a, gfc_expr *p)
 {
   gfc_expr *result;
-  mpfr_t tmp;
   int kind;
 
   if (a->expr_type != EXPR_CONSTANT || p->expr_type != EXPR_CONSTANT)
@@ -4308,12 +4302,17 @@ gfc_simplify_modulo (gfc_expr *a, gfc_expr *p)
          }
 
        gfc_set_model_kind (kind);
-       mpfr_init (tmp);
-       mpfr_div (tmp, a->value.real, p->value.real, GFC_RND_MODE);
-       mpfr_floor (tmp, tmp);
-       mpfr_mul (tmp, tmp, p->value.real, GFC_RND_MODE);
-       mpfr_sub (result->value.real, a->value.real, tmp, GFC_RND_MODE);
-       mpfr_clear (tmp);
+       mpfr_fmod (result->value.real, a->value.real, p->value.real, 
+                  GFC_RND_MODE);
+       if (mpfr_cmp_ui (result->value.real, 0) != 0)
+         {
+           if (mpfr_signbit (a->value.real) != mpfr_signbit (p->value.real))
+             mpfr_add (result->value.real, result->value.real, p->value.real,
+                       GFC_RND_MODE);
+         }
+       else
+         mpfr_copysign (result->value.real, result->value.real, 
+                        p->value.real, GFC_RND_MODE);
        break;
 
       default:
index ab4f47fc5d30f68c9320d328afe6630d2d49c0df..bfbebf3269b6d8e0b5406c10c19a42f2ea92daa6 100644 (file)
@@ -1719,21 +1719,24 @@ gfc_conv_intrinsic_cmplx (gfc_se * se, gfc_expr * expr, int both)
   se->expr = fold_build2_loc (input_location, COMPLEX_EXPR, type, real, imag);
 }
 
+
 /* Remainder function MOD(A, P) = A - INT(A / P) * P
-                      MODULO(A, P) = A - FLOOR (A / P) * P  */
-/* TODO: MOD(x, 0)  */
+                      MODULO(A, P) = A - FLOOR (A / P) * P  
+
+   The obvious algorithms above are numerically instable for large
+   arguments, hence these intrinsics are instead implemented via calls
+   to the fmod family of functions.  It is the responsibility of the
+   user to ensure that the second argument is non-zero.  */
 
 static void
 gfc_conv_intrinsic_mod (gfc_se * se, gfc_expr * expr, int modulo)
 {
   tree type;
-  tree itype;
   tree tmp;
   tree test;
   tree test2;
   tree fmod;
-  mpfr_t huge;
-  int n, ikind;
+  tree zero;
   tree args[2];
 
   gfc_conv_intrinsic_function_args (se, expr, args, 2);
@@ -1757,16 +1760,15 @@ gfc_conv_intrinsic_mod (gfc_se * se, gfc_expr * expr, int modulo)
       /* Check if we have a builtin fmod.  */
       fmod = gfc_builtin_decl_for_float_kind (BUILT_IN_FMOD, expr->ts.kind);
 
-      /* Use it if it exists.  */
-      if (fmod != NULL_TREE)
-       {
-         tmp = build_addr (fmod, current_function_decl);
-         se->expr = build_call_array_loc (input_location,
+      /* The builtin should always be available.  */
+      gcc_assert (fmod != NULL_TREE);
+
+      tmp = build_addr (fmod, current_function_decl);
+      se->expr = build_call_array_loc (input_location,
                                       TREE_TYPE (TREE_TYPE (fmod)),
                                        tmp, 2, args);
-         if (modulo == 0)
-           return;
-       }
+      if (modulo == 0)
+       return;
 
       type = TREE_TYPE (args[0]);
 
@@ -1774,16 +1776,31 @@ gfc_conv_intrinsic_mod (gfc_se * se, gfc_expr * expr, int modulo)
       args[1] = gfc_evaluate_now (args[1], &se->pre);
 
       /* Definition:
-        modulo = arg - floor (arg/arg2) * arg2, so
-               = test ? fmod (arg, arg2) : fmod (arg, arg2) + arg2, 
-        where
-         test  = (fmod (arg, arg2) != 0) && ((arg < 0) xor (arg2 < 0))
-        thereby avoiding another division and retaining the accuracy
-        of the builtin function.  */
-      if (fmod != NULL_TREE && modulo)
+        modulo = arg - floor (arg/arg2) * arg2
+
+        In order to calculate the result accurately, we use the fmod
+        function as follows.
+        
+        res = fmod (arg, arg2);
+        if (res)
+          {
+            if ((arg < 0) xor (arg2 < 0))
+              res += arg2;
+          }
+        else
+          res = copysign (0., arg2);
+
+        => As two nested ternary exprs:
+
+        res = res ? (((arg < 0) xor (arg2 < 0)) ? res + arg2 : res) 
+              : copysign (0., arg2);
+
+      */
+
+      zero = gfc_build_const (type, integer_zero_node);
+      tmp = gfc_evaluate_now (se->expr, &se->pre);
+      if (!flag_signed_zeros)
        {
-         tree zero = gfc_build_const (type, integer_zero_node);
-         tmp = gfc_evaluate_now (se->expr, &se->pre);
          test = fold_build2_loc (input_location, LT_EXPR, boolean_type_node,
                                  args[0], zero);
          test2 = fold_build2_loc (input_location, LT_EXPR, boolean_type_node,
@@ -1796,50 +1813,35 @@ gfc_conv_intrinsic_mod (gfc_se * se, gfc_expr * expr, int modulo)
                                  boolean_type_node, test, test2);
          test = gfc_evaluate_now (test, &se->pre);
          se->expr = fold_build3_loc (input_location, COND_EXPR, type, test,
-                                 fold_build2_loc (input_location, PLUS_EXPR,
-                                                  type, tmp, args[1]), tmp);
-         return;
+                                     fold_build2_loc (input_location, 
+                                                      PLUS_EXPR,
+                                                      type, tmp, args[1]), 
+                                     tmp);
        }
-
-      /* If we do not have a built_in fmod, the calculation is going to
-        have to be done longhand.  */
-      tmp = fold_build2_loc (input_location, RDIV_EXPR, type, args[0], args[1]);
-
-      /* Test if the value is too large to handle sensibly.  */
-      gfc_set_model_kind (expr->ts.kind);
-      mpfr_init (huge);
-      n = gfc_validate_kind (BT_INTEGER, expr->ts.kind, true);
-      ikind = expr->ts.kind;
-      if (n < 0)
+      else
        {
-         n = gfc_validate_kind (BT_INTEGER, gfc_max_integer_kind, false);
-         ikind = gfc_max_integer_kind;
+         tree expr1, copysign, cscall;
+         copysign = gfc_builtin_decl_for_float_kind (BUILT_IN_COPYSIGN, 
+                                                     expr->ts.kind);
+         test = fold_build2_loc (input_location, LT_EXPR, boolean_type_node,
+                                 args[0], zero);
+         test2 = fold_build2_loc (input_location, LT_EXPR, boolean_type_node,
+                                  args[1], zero);
+         test2 = fold_build2_loc (input_location, TRUTH_XOR_EXPR,
+                                  boolean_type_node, test, test2);
+         expr1 = fold_build3_loc (input_location, COND_EXPR, type, test2,
+                                  fold_build2_loc (input_location, 
+                                                   PLUS_EXPR,
+                                                   type, tmp, args[1]), 
+                                  tmp);
+         test = fold_build2_loc (input_location, NE_EXPR, boolean_type_node,
+                                 tmp, zero);
+         cscall = build_call_expr_loc (input_location, copysign, 2, zero, 
+                                       args[1]);
+         se->expr = fold_build3_loc (input_location, COND_EXPR, type, test,
+                                     expr1, cscall);
        }
-      mpfr_set_z (huge, gfc_integer_kinds[n].huge, GFC_RND_MODE);
-      test = gfc_conv_mpfr_to_tree (huge, expr->ts.kind, 0);
-      test2 = fold_build2_loc (input_location, LT_EXPR, boolean_type_node,
-                              tmp, test);
-
-      mpfr_neg (huge, huge, GFC_RND_MODE);
-      test = gfc_conv_mpfr_to_tree (huge, expr->ts.kind, 0);
-      test = fold_build2_loc (input_location, GT_EXPR, boolean_type_node, tmp,
-                             test);
-      test2 = fold_build2_loc (input_location, TRUTH_AND_EXPR,
-                              boolean_type_node, test, test2);
-
-      itype = gfc_get_int_type (ikind);
-      if (modulo)
-       tmp = build_fix_expr (&se->pre, tmp, itype, RND_FLOOR);
-      else
-       tmp = build_fix_expr (&se->pre, tmp, itype, RND_TRUNC);
-      tmp = convert (type, tmp);
-      tmp = fold_build3_loc (input_location, COND_EXPR, type, test2, tmp,
-                            args[0]);
-      tmp = fold_build2_loc (input_location, MULT_EXPR, type, tmp, args[1]);
-      se->expr = fold_build2_loc (input_location, MINUS_EXPR, type, args[0],
-                                 tmp);
-      mpfr_clear (huge);
-      break;
+      return;
 
     default:
       gcc_unreachable ();
index cb5486c20aa4bd44ff46a3318049229f0367d35d..c954165f0fda141e746b82c0fd7f1329b62f9120 100644 (file)
@@ -1,3 +1,10 @@
+2012-05-05  Janne Blomqvist  <jb@gcc.gnu.org>
+
+       PR fortran/49010
+       PR fortran/24518
+       * gfortran.dg/mod_sign0_1.f90: New test.
+       * gfortran.dg/mod_large_1.f90: New test.
+
 2012-05-04  Tobias Burnus  <burnus@net-b.de>
 
        PR fortran/53175
diff --git a/gcc/testsuite/gfortran.dg/mod_large_1.f90 b/gcc/testsuite/gfortran.dg/mod_large_1.f90
new file mode 100644 (file)
index 0000000..1047ad6
--- /dev/null
@@ -0,0 +1,16 @@
+! { dg-do run }
+! PR fortran/24518 
+! MOD/MODULO of large arguments.
+! The naive algorithm goes pear-shaped for large arguments, instead
+! use fmod.
+! Here we test only with constant arguments (evaluated with
+! mpfr_fmod), as we don't want to cause failures on targets with a
+! crappy libm.
+program mod_large_1
+  implicit none
+  real :: r1
+  r1 = mod (1e22, 1.7)
+  if (abs(r1 - 0.995928764) > 1e-5) call abort
+  r1 = modulo (1e22, -1.7)
+  if (abs(r1 + 0.704071283) > 1e-5) call abort
+end program mod_large_1
diff --git a/gcc/testsuite/gfortran.dg/mod_sign0_1.f90 b/gcc/testsuite/gfortran.dg/mod_sign0_1.f90
new file mode 100644 (file)
index 0000000..61ef5fd
--- /dev/null
@@ -0,0 +1,54 @@
+! { dg-do run }
+! PR fortran/49010 
+! MOD/MODULO sign of zero.
+
+! We wish to provide the following guarantees:
+
+! MOD(A, P): The result has the sign of A and a magnitude less than
+! that of P.  
+
+! MODULO(A, P): The result has the sign of P and a magnitude less than
+! that of P.
+
+! Here we test only with constant arguments (evaluated with
+! mpfr_fmod), as we don't want to cause failures on targets with a
+! crappy libm. But, a target where fmod follows C99 Annex F is
+! fine. Also, targets where GCC inline expands fmod (such as x86(-64))
+! are also fine.
+program mod_sign0_1
+  implicit none
+  real :: r, t
+
+  r = mod (4., 2.)
+  t = sign (1., r)
+  if (t < 0.) call abort
+
+  r = modulo (4., 2.)
+  t = sign (1., r)
+  if (t < 0.) call abort
+
+  r = mod (-4., 2.)
+  t = sign (1., r)
+  if (t > 0.) call abort
+
+  r = modulo (-4., 2.)
+  t = sign (1., r)
+  if (t < 0.) call abort
+
+  r = mod (4., -2.)
+  t = sign (1., r)
+  if (t < 0.) call abort
+
+  r = modulo (4., -2.)
+  t = sign (1., r)
+  if (t > 0.) call abort
+
+  r = mod (-4., -2.)
+  t = sign (1., r)
+  if (t > 0.) call abort
+
+  r = modulo (-4., -2.)
+  t = sign (1., r)
+  if (t > 0.) call abort
+
+end program mod_sign0_1