summaryrefslogtreecommitdiff
path: root/gcc/fortran
diff options
context:
space:
mode:
authorjb <jb@138bc75d-0d04-0410-961f-82ee72b054a4>2012-05-05 07:59:22 +0000
committerjb <jb@138bc75d-0d04-0410-961f-82ee72b054a4>2012-05-05 07:59:22 +0000
commitfa0323b8cddaae96ccf2e8acc414baa926d4fa1f (patch)
tree8d0572d604c2f2a46fc172d68dceeb1635b87c47 /gcc/fortran
parenta291e3b6538a0697986d1a7d22aa220829998bab (diff)
downloadgcc-fa0323b8cddaae96ccf2e8acc414baa926d4fa1f.tar.gz
PR 49010,24518 MOD/MODULO fixes.
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. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@187191 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc/fortran')
-rw-r--r--gcc/fortran/ChangeLog12
-rw-r--r--gcc/fortran/intrinsic.texi30
-rw-r--r--gcc/fortran/simplify.c27
-rw-r--r--gcc/fortran/trans-intrinsic.c128
4 files changed, 110 insertions, 87 deletions
diff --git a/gcc/fortran/ChangeLog b/gcc/fortran/ChangeLog
index d1cb4294be6..a9b4195499d 100644
--- a/gcc/fortran/ChangeLog
+++ b/gcc/fortran/ChangeLog
@@ -1,5 +1,17 @@
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
algorithm for choosing temp directory.
diff --git a/gcc/fortran/intrinsic.texi b/gcc/fortran/intrinsic.texi
index 294818e43d0..9bc36d7d415 100644
--- a/gcc/fortran/intrinsic.texi
+++ b/gcc/fortran/intrinsic.texi
@@ -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
diff --git a/gcc/fortran/simplify.c b/gcc/fortran/simplify.c
index 706dab440ce..1578db19b94 100644
--- a/gcc/fortran/simplify.c
+++ b/gcc/fortran/simplify.c
@@ -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:
diff --git a/gcc/fortran/trans-intrinsic.c b/gcc/fortran/trans-intrinsic.c
index ab4f47fc5d3..bfbebf3269b 100644
--- a/gcc/fortran/trans-intrinsic.c
+++ b/gcc/fortran/trans-intrinsic.c
@@ -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 ();