Problem with nearest(tiny(o),-1.0)/2.0
Steve Kargl
sgk@troutmask.apl.washington.edu
Wed Apr 5 20:45:00 GMT 2006
On Tue, Apr 04, 2006 at 11:57:12AM +0200, Dominique Dhumieres wrote:
> The following program
>
> program chop
> real o, t, td, tu, x, y
> o = 1.
> t = tiny(o)
> td = nearest(t,-1.0)
> x = td/2.0
> y = nearest(tiny(o),-1.0)/2.0
> print *, x, y, x - y
> end program chop
>
> gives
>
> 5.8774718E-39 5.8774704E-39 1.4012985E-45
>
> on OSX 10.3.9 and GNU Fortran 95 (GCC) 4.2.0 20060401 (experimental).
> I.e, nearest(tiny(o),-1.0)/2.0 does not give the same result as
>
> td = nearest(t,-1.0)
> x = td/2.0
>
With the attached patch, I get
troutmask:sgk[273] ./z
5.8774718E-39 5.8774718E-39 0.000000
This patch requires MPFR 2.2.0 or higher. I'm still testing
this patch, so it may change. I also need to update configure
to look for a correct version of MPFR.
--
Steve
-------------- next part --------------
Index: simplify.c
===================================================================
--- simplify.c (revision 112712)
+++ simplify.c (working copy)
@@ -2385,71 +2385,42 @@ gfc_simplify_nearest (gfc_expr * x, gfc_
{
gfc_expr *result;
mpfr_t tmp;
- int direction, sgn;
+ int sgn;
+ mp_exp_t emin, emax;
if (x->expr_type != EXPR_CONSTANT || s->expr_type != EXPR_CONSTANT)
return NULL;
- gfc_set_model_kind (x->ts.kind);
- result = gfc_copy_expr (x);
-
- direction = mpfr_sgn (s->value.real);
-
- if (direction == 0)
+ if (mpfr_sgn (s->value.real) == 0)
{
- gfc_error ("Second argument of NEAREST at %L may not be zero",
- &s->where);
- gfc_free (result);
+ gfc_error ("Second argument of NEAREST at %L shall not be zero", &s->where);
return &gfc_bad_expr;
}
- /* TODO: Use mpfr_nextabove and mpfr_nextbelow once we move to a
- newer version of mpfr. */
+ gfc_set_model_kind (x->ts.kind);
- sgn = mpfr_sgn (x->value.real);
+ sgn = gfc_validate_kind (BT_REAL, x->ts.kind, 0);
- if (sgn == 0)
- {
- int k = gfc_validate_kind (BT_REAL, x->ts.kind, 0);
-
- if (direction > 0)
- mpfr_add (result->value.real,
- x->value.real, gfc_real_kinds[k].subnormal, GFC_RND_MODE);
- else
- mpfr_sub (result->value.real,
- x->value.real, gfc_real_kinds[k].subnormal, GFC_RND_MODE);
- }
- else
- {
- if (sgn < 0)
- {
- direction = -direction;
- mpfr_neg (result->value.real, result->value.real, GFC_RND_MODE);
- }
-
- if (direction > 0)
- mpfr_add_one_ulp (result->value.real, GFC_RND_MODE);
- else
- {
- /* In this case the exponent can shrink, which makes us skip
- over one number because we subtract one ulp with the
- larger exponent. Thus we need to compensate for this. */
- mpfr_init_set (tmp, result->value.real, GFC_RND_MODE);
-
- mpfr_sub_one_ulp (result->value.real, GFC_RND_MODE);
- mpfr_add_one_ulp (result->value.real, GFC_RND_MODE);
-
- /* If we're back to where we started, the spacing is one
- ulp, and we get the correct result by subtracting. */
- if (mpfr_cmp (tmp, result->value.real) == 0)
- mpfr_sub_one_ulp (result->value.real, GFC_RND_MODE);
+ result = gfc_copy_expr (x);
+
+ /* Save current values of emin and emax. */
+ emin = mpfr_get_emin ();
+ emax = mpfr_get_emax ();
+
+ /* Set emin and emax for the current model number. */
+ mpfr_set_emin ((mp_exp_t) gfc_real_kinds[sgn].min_exponent - 1);
+ mpfr_set_emax ((mp_exp_t) gfc_real_kinds[sgn].max_exponent - 1);
+
+ sgn = mpfr_sgn (s->value.real);
+ mpfr_init (tmp);
+ mpfr_set_inf (tmp, sgn);
+ mpfr_nexttoward (result->value.real, tmp);
+ mpfr_subnormalize (result->value.real, 0, GFC_RND_MODE);
- mpfr_clear (tmp);
- }
+ mpfr_set_emin (emin);
+ mpfr_set_emax (emax);
- if (sgn < 0)
- mpfr_neg (result->value.real, result->value.real, GFC_RND_MODE);
- }
+ mpfr_clear(tmp);
return range_check (result, "NEAREST");
}
More information about the Fortran
mailing list