This is the mail archive of the egcs@egcs.cygnus.com mailing list for the EGCS project. See the EGCS home page for more information.


[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index] [Subject Index] [Author Index] [Thread Index]

Re: GAS IEEE FP bug



Gary Thomas <gdt@linuxppc.org> writes:

|> There is an error in all versions of GAS which use IEEE floating point.
|> The version I was testing is: GNU assembler version 2.9.1
|> 
|> This constant is improperly assembled:
|>    9 0020 00000000              .float 0d1.40129846432481707092e-45
|> The correct value (Big Endian) would be
|>    9 0020 00000001              .float 0d1.40129846432481707092e-45
|> The correct value (Little Endian) would be
|>    9 0020 01000000              .float 0d1.40129846432481707092e-45
|> 
|> Note: this is the smallest possible IEEE unnormalized "float" value.
|> 
|> A simple patch for this (seems to work at least) is:
|> 
|> --- binutils-2.9.1/gas/config/atof-ieee.c~      Fri May  1 16:44:33 1998
|> +++ binutils-2.9.1/gas/config/atof-ieee.c       Sat Mar  6 06:57:08 1999
|> @@ -444,6 +444,8 @@
|>        unget_bits (1);
|>        num_bits = -exponent_4;
|>        prec_bits = LITTLENUM_NUMBER_OF_BITS * precision - (exponent_bits + 1 + num_bits);
|> +      // There is 1 more bit of precision here (hidden)
|> +      prec_bits += 1;
|>  #ifdef TC_I386
|>        if (precision == X_PRECISION && exponent_bits == 15)
|>         {

This is wrong.  The hidden zero has already been accounted for, since the
exponent bias is one less in a denormal.  The bug is actually in the
rounding code.  It should never ignore a set round bit, even if the
prec_bits bitfield already has all bits set, especially if prec_bits == 0.

There is also another bug here: the assembler rejects the smallest
extended precision number, which is 0d1.822599765941237301264e-4951 on the
m68k.  If prec_bits is zero then the round bit is always set and the
mantissa will be rounded up.


1999-03-11  Andreas Schwab  <schwab@issan.cs.uni-dortmund.de>

	* config/atof-ieee.c (gen_to_words): Correctly round a
	denormalized number.  Fix off-by-one in range checking for
	exponent in a denormal.

--- binutils-2.9.4/gas/config/atof-ieee.c.~1~	Thu Apr 23 23:55:07 1998
+++ binutils-2.9.4/gas/config/atof-ieee.c	Thu Mar 11 02:06:35 1999
@@ -460,7 +460,7 @@
 	  /* Bigger than one littlenum */
 	  num_bits -= (LITTLENUM_NUMBER_OF_BITS - 1) - exponent_bits;
 	  *lp++ = word1;
-	  if (num_bits + exponent_bits + 1 >= precision * LITTLENUM_NUMBER_OF_BITS)
+	  if (num_bits + exponent_bits + 1 > precision * LITTLENUM_NUMBER_OF_BITS)
 	    {
 	      /* Exponent overflow */
 	      make_invalid_floating_point_number (words);
@@ -501,7 +501,7 @@
       if (next_bits (1))
 	{
 	  --lp;
-	  if (prec_bits > LITTLENUM_NUMBER_OF_BITS)
+	  if (prec_bits >= LITTLENUM_NUMBER_OF_BITS)
 	    {
 	      int n = 0;
 	      int tmp_bits;
@@ -515,7 +515,19 @@
 		  --n;
 		  tmp_bits -= LITTLENUM_NUMBER_OF_BITS;
 		}
-	      if (tmp_bits > LITTLENUM_NUMBER_OF_BITS || (lp[n] & mask[tmp_bits]) != mask[tmp_bits])
+	      if (tmp_bits > LITTLENUM_NUMBER_OF_BITS
+		  || (lp[n] & mask[tmp_bits]) != mask[tmp_bits]
+		  || (prec_bits != (precision * LITTLENUM_NUMBER_OF_BITS
+				    - exponent_bits - 1)
+#ifdef TC_I386
+		      /* An extended precision float with only the integer
+			 bit set would be invalid.  That must be converted
+			 to the smallest normalized number.  */
+		      && !(precision == X_PRECISION
+			   && prec_bits == (precision * LITTLENUM_NUMBER_OF_BITS
+					    - exponent_bits - 2))
+#endif
+		      ))
 		{
 		  unsigned long carry;
 
@@ -539,11 +551,18 @@
 			    << ((LITTLENUM_NUMBER_OF_BITS - 1)
 				- exponent_bits));
 		  *lp++ = word1;
+#ifdef TC_I386
+		  /* Set the integer bit in the extended precision format.
+		     This cannot happen on the m68k where the mantissa
+		     just overflows into the integer bit above.  */
+		  if (precision == X_PRECISION)
+		    *lp++ = 1 << (LITTLE_NUMBER_OF_BITS - 1);
+#endif
 		  while (lp < words_end)
 		    *lp++ = 0;
 		}
 	    }
-	  else if ((*lp & mask[prec_bits]) != mask[prec_bits])
+	  else
 	    *lp += 1;
 	}