if (*src)
*dest = op (*dest, gfc_copy_expr (*src));
+ if (post_op)
+ *dest = post_op (*dest, *dest);
+
count[0]++;
base += sstride[0];
dest += dstride[0];
result_ctor = gfc_constructor_first (result->value.constructor);
for (i = 0; i < resultsize; ++i)
{
- if (post_op)
- result_ctor->expr = post_op (result_ctor->expr, resultvec[i]);
- else
- result_ctor->expr = resultvec[i];
+ result_ctor->expr = resultvec[i];
result_ctor = gfc_constructor_next (result_ctor);
}
return simplify_nint ("IDNINT", e, NULL);
}
+static int norm2_scale;
static gfc_expr *
-add_squared (gfc_expr *result, gfc_expr *e)
+norm2_add_squared (gfc_expr *result, gfc_expr *e)
{
mpfr_t tmp;
&& result->expr_type == EXPR_CONSTANT);
gfc_set_model_kind (result->ts.kind);
+ int index = gfc_validate_kind (BT_REAL, result->ts.kind, false);
+ mpfr_exp_t exp;
+ if (mpfr_regular_p (result->value.real))
+ {
+ exp = mpfr_get_exp (result->value.real);
+ /* If result is getting close to overflowing, scale down. */
+ if (exp >= gfc_real_kinds[index].max_exponent - 4
+ && norm2_scale <= gfc_real_kinds[index].max_exponent - 2)
+ {
+ norm2_scale += 2;
+ mpfr_div_ui (result->value.real, result->value.real, 16,
+ GFC_RND_MODE);
+ }
+ }
+
mpfr_init (tmp);
- mpfr_pow_ui (tmp, e->value.real, 2, GFC_RND_MODE);
+ if (mpfr_regular_p (e->value.real))
+ {
+ exp = mpfr_get_exp (e->value.real);
+ /* If e**2 would overflow or close to overflowing, scale down. */
+ if (exp - norm2_scale >= gfc_real_kinds[index].max_exponent / 2 - 2)
+ {
+ int new_scale = gfc_real_kinds[index].max_exponent / 2 + 4;
+ mpfr_set_ui (tmp, 1, GFC_RND_MODE);
+ mpfr_set_exp (tmp, new_scale - norm2_scale);
+ mpfr_div (result->value.real, result->value.real, tmp, GFC_RND_MODE);
+ mpfr_div (result->value.real, result->value.real, tmp, GFC_RND_MODE);
+ norm2_scale = new_scale;
+ }
+ }
+ if (norm2_scale)
+ {
+ mpfr_set_ui (tmp, 1, GFC_RND_MODE);
+ mpfr_set_exp (tmp, norm2_scale);
+ mpfr_div (tmp, e->value.real, tmp, GFC_RND_MODE);
+ }
+ else
+ mpfr_set (tmp, e->value.real, GFC_RND_MODE);
+ mpfr_pow_ui (tmp, tmp, 2, GFC_RND_MODE);
mpfr_add (result->value.real, result->value.real, tmp,
GFC_RND_MODE);
mpfr_clear (tmp);
static gfc_expr *
-do_sqrt (gfc_expr *result, gfc_expr *e)
+norm2_do_sqrt (gfc_expr *result, gfc_expr *e)
{
gcc_assert (e->ts.type == BT_REAL && e->expr_type == EXPR_CONSTANT);
gcc_assert (result->ts.type == BT_REAL
&& result->expr_type == EXPR_CONSTANT);
- mpfr_set (result->value.real, e->value.real, GFC_RND_MODE);
+ if (result != e)
+ mpfr_set (result->value.real, e->value.real, GFC_RND_MODE);
mpfr_sqrt (result->value.real, result->value.real, GFC_RND_MODE);
+ if (norm2_scale && mpfr_regular_p (result->value.real))
+ {
+ mpfr_t tmp;
+ mpfr_init (tmp);
+ mpfr_set_ui (tmp, 1, GFC_RND_MODE);
+ mpfr_set_exp (tmp, norm2_scale);
+ mpfr_mul (result->value.real, result->value.real, tmp, GFC_RND_MODE);
+ mpfr_clear (tmp);
+ }
+ norm2_scale = 0;
+
return result;
}
if (size_zero)
return result;
+ norm2_scale = 0;
if (!dim || e->rank == 1)
{
result = simplify_transformation_to_scalar (result, e, NULL,
- add_squared);
+ norm2_add_squared);
mpfr_sqrt (result->value.real, result->value.real, GFC_RND_MODE);
+ if (norm2_scale && mpfr_regular_p (result->value.real))
+ {
+ mpfr_t tmp;
+ mpfr_init (tmp);
+ mpfr_set_ui (tmp, 1, GFC_RND_MODE);
+ mpfr_set_exp (tmp, norm2_scale);
+ mpfr_mul (result->value.real, result->value.real, tmp, GFC_RND_MODE);
+ mpfr_clear (tmp);
+ }
+ norm2_scale = 0;
}
else
result = simplify_transformation_to_array (result, e, dim, NULL,
- add_squared, &do_sqrt);
+ norm2_add_squared,
+ norm2_do_sqrt);
return result;
}