[PATCH/gfortran] Fix rounding in constant folding

Steve Kargl sgk@troutmask.apl.washington.edu
Sun Apr 10 05:57:00 GMT 2005


On Sat, Apr 09, 2005 at 07:45:01PM -0700, Steve Kargl wrote:
> On Sun, Apr 10, 2005 at 01:49:31AM +0200, Tobias Schl?ter wrote:
> > !   direction = mpfr_cmp_ui (s->value.real, 0);
> 
>       direction = mpfr_sgn (s->value.real);
> 
> > !   if (direction > 0)
> >       {
> > !       /* mpfr_add_one_ulp (result->value.real);  (after initializing
> > !        it to x->value.real, of course) didn't do the trick.  */
> > !       do
> >         {
> > !         mpfr_add (result->value.real, x->value.real, ulp, GFC_RND_MODE);
> > !         mpfr_mul_ui (ulp, ulp, 2, GFC_RND_MODE);
> 
> How many times do you go through the loop?  I would think that
> you would want to add another ulp each time through.  Right now
> you have ulp, 2*ulp, 4*ulp, 8*ulp, etc.
> 
> BTW, the algorithm that you're using is what I would do.
> Also, note GFC_RND_MODE gives round-to-nearest behavior.
> You can set this to one of
> 
>    * `GMP_RNDN': round to nearest
>    * `GMP_RNDZ': round towards zero
>    * `GMP_RNDU': round towards plus infinity
>    * `GMP_RNDD': round towards minus infinity

I used mpfr_add_one_ulp and mpfr_sub_one_ulp, I believe 
MPFR is doing the right.  I instrumented gfc_simplify_nearest
with 

  mpfr_print_binary (x->value.real);
  printf("\n");
  mpfr_print_binary (result->value.real);
  printf("\n");

which yields 

0.100000000000000000000000[00000000]E0
0.111111111111111111111110[00000000]E-1
0.100000000000000000000000[00000000]E0
0.111111111111111111111110[00000000]E-1
0.100000000000000000000000[00000000]E0
0.111111111111111111111110[00000000]E-1
 
Your program is giving

kargl[238] ./z
  0.5000000     T           0
  0.4999999     T           0
  0.5000000     T           0

so there may be a rounding problem with IO in the runtime library.
I've attached my diff for your amusement.

-- 
Steve
-------------- next part --------------
Index: simplify.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/fortran/simplify.c,v
retrieving revision 1.21
diff -c -p -r1.21 simplify.c
*** simplify.c	7 Apr 2005 18:26:37 -0000	1.21
--- simplify.c	10 Apr 2005 05:52:56 -0000
*************** gfc_expr *
*** 2311,2369 ****
  gfc_simplify_nearest (gfc_expr * x, gfc_expr * s)
  {
    gfc_expr *result;
!   float rval;
!   double val, eps;
!   int p, i, k, match_float;
! 
!   /* FIXME: This implementation is dopey and probably not quite right,
!      but it's a start.  */
  
!   if (x->expr_type != EXPR_CONSTANT)
      return NULL;
  
!   k = gfc_validate_kind (x->ts.type, x->ts.kind, false);
  
!   result = gfc_constant_result (x->ts.type, x->ts.kind, &x->where);
  
!   val = mpfr_get_d (x->value.real, GFC_RND_MODE);
!   p = gfc_real_kinds[k].digits;
  
!   eps = 1.;
!   for (i = 1; i < p; ++i)
!     {
!       eps = eps / 2.;
!     }
! 
!   /* TODO we should make sure that 'float' matches kind 4 */
!   match_float = gfc_real_kinds[k].kind == 4;
!   if (mpfr_cmp_ui (s->value.real, 0) > 0)
!     {
!       if (match_float)
! 	{
! 	  rval = (float) val;
! 	  rval = rval + eps;
! 	  mpfr_set_d (result->value.real, rval, GFC_RND_MODE);
! 	}
!       else
! 	{
! 	  val = val + eps;
! 	  mpfr_set_d (result->value.real, val, GFC_RND_MODE);
! 	}
!     }
!   else if (mpfr_cmp_ui (s->value.real, 0) < 0)
!     {
!       if (match_float)
! 	{
! 	  rval = (float) val;
! 	  rval = rval - eps;
! 	  mpfr_set_d (result->value.real, rval, GFC_RND_MODE);
! 	}
!       else
! 	{
! 	  val = val - eps;
! 	  mpfr_set_d (result->value.real, val, GFC_RND_MODE);
! 	}
!     }
    else
      {
        gfc_error ("Invalid second argument of NEAREST at %L", &s->where);
--- 2263,2283 ----
  gfc_simplify_nearest (gfc_expr * x, gfc_expr * s)
  {
    gfc_expr *result;
!   int cmp;
  
!   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);
  
!   cmp = mpfr_sgn (s->value.real);
  
!   if (cmp > 0)
!     mpfr_add_one_ulp (result->value.real, GFC_RND_MODE);
!   else if (cmp < 0)
!     mpfr_sub_one_ulp (result->value.real, GFC_RND_MODE);
    else
      {
        gfc_error ("Invalid second argument of NEAREST at %L", &s->where);


More information about the Gcc-patches mailing list