[gnu.gdb.bug] problems with GDB 3.5's display of IEEE floating point values

eggert@twinsun.com (Paul Eggert) (02/09/90)

GDB 3.5 has problems displaying IEEE floating point values, particularly NaNs.

* The test for whether a number is a NaN is flawed: the expression highhalf &
0xfffff == 0 in is_nan() should be (highhalf & 0xfffff) == 0.

* The signs of NaNs are not displayed.  The fractions of NaNs (which give
machine-dependent information about what generated the NaN) are not displayed.

* Several m-*.h files should #define IEEE_FLOAT but don't, e.g. m-news.h,
m-sun3.h.

* Only 16 digits of a double are printed; the IEEE standard requires 17 digits
for maximum precision.

Patches for these problems are enclosed below.  The patches avoid the
IEEE_FLOAT problem by using a simple dynamic test to see whether a number X is
a NaN; if (X!=X), it's a NaN, otherwise it isn't.  This trick is documented in
the IEEE floating point standard, so it works for IEEE floating point.  If some
non-IEEE floating point format has a value X where X!=X, the ``else if (doub !=
doub) { ... }'' code below needs to be surrounded by ``#ifdef IEEE_FLOAT ...
#endif''; but I suspect that this is not the case, and that IEEE_FLOAT can be
removed from the m-*.h files.

*** old/printcmd.c	Fri Jan 19 19:32:54 1990
--- new/printcmd.c	Thu Feb  8 22:56:08 1990
***************
*** 63,68 ****
--- 63,69 ----
  
  void do_displays ();
  void print_address ();
+ void print_floating ();
  void print_scalar_formatted ();
  
  
***************
*** 355,377 ****
  	type = builtin_type_float;
        else if (len == sizeof (double))
  	type = builtin_type_double;
! #ifdef IEEE_FLOAT
!       if (is_nan (type, valaddr))
! 	{
! 	  fprintf_filtered (stream, "Nan");
! 	  break;
! 	}
! #endif
!       {
! 	double doub;
! 	int inv;
! 	
! 	doub = unpack_double (type, valaddr, &inv);
! 	if (inv)
! 	  fprintf_filtered (stream, "Invalid float value");
! 	else
! 	  fprintf_filtered (stream, len > 4 ? "%.16g" : "%.6g", doub);
!       }
        break;
  
      case 0:
--- 356,362 ----
  	type = builtin_type_float;
        else if (len == sizeof (double))
  	type = builtin_type_double;
!       print_floating(valaddr, type, stream);
        break;
  
      case 0:
***************
*** 380,385 ****
--- 365,423 ----
      default:
        error ("Undefined output format \"%c\".", format);
      }
+ }
+ 
+ /* Print a floating point value of type TYPE, pointed to in GDB by VALADDR,
+    on STREAM.  */
+ 
+ void
+ print_floating(valaddr, type, stream)
+      char *valaddr;
+      struct type *type;
+      FILE *stream;
+ {
+   double doub;
+   int inv;
+   int len = TYPE_LENGTH (type);
+   
+   doub = unpack_double (type, valaddr, &inv);
+   if (inv)
+     fprintf_filtered (stream, "Invalid float value");
+   else if (doub != doub)
+     {
+       /* Surely it is an IEEE floating point NaN. */
+ 
+       long low, high, *arg = (long *)valaddr;	/* ASSUMED 32 BITS */
+       int nonneg;
+ 
+       if (len <= sizeof(float))
+ 	{
+ 	  /* It's single precision. */
+ 	  low = *arg;
+ 	  nonneg  =  low >= 0;
+ 	  low &= 0x7fffff;
+ 	  high = 0;
+ 	}
+       else
+ 	{
+ 	  /* It's double precision.
+ 	     Get the high and low words of the fraction.
+ 	     Distinguish big and little-endian machines.  */
+ #ifdef WORDS_BIG_ENDIAN
+ 	  low = arg[1], high = arg[0];
+ #else
+ 	  low = arg[0], high = arg[1];
+ #endif
+ 	  nonneg  =  high >= 0;
+ 	  high &= 0xfffff;
+ 	}
+       if (high)
+ 	fprintf_filtered (stream, "-NaN(0x%lx%.8lx)" + nonneg, high, low);
+       else
+ 	fprintf_filtered (stream, "-NaN(0x%lx)" + nonneg, low);
+     }
+   else
+     fprintf_filtered (stream, len <= sizeof(float) ? "%.6g" : "%.17g", doub);
  }
  
  /* Specify default address for `x' command.

*** old/valprint.c	Thu Feb  1 14:48:29 1990
--- new/valprint.c	Thu Feb  8 22:56:12 1990
***************
*** 714,741 ****
  
      case TYPE_CODE_FLT:
        if (format)
! 	{
! 	  print_scalar_formatted (valaddr, type, format, 0, stream);
! 	  break;
! 	}
! #ifdef IEEE_FLOAT
!       if (is_nan ((char *) valaddr, TYPE_LENGTH (type)))
! 	{
! 	  fprintf_filtered (stream, "NaN");
! 	  break;
! 	}
! #endif
!       {
! 	double doub;
! 	int inv;
! 
! 	doub = unpack_double (type, valaddr, &inv);
! 	if (inv)
! 	  fprintf_filtered (stream, "Invalid float value");
! 	else
! 	  fprintf_filtered (stream,
! 			    TYPE_LENGTH (type) <= 4? "%.6g": "%.16g", doub);
!       }
        break;
  
      case TYPE_CODE_VOID:
--- 714,722 ----
  
      case TYPE_CODE_FLT:
        if (format)
! 	print_scalar_formatted (valaddr, type, format, 0, stream);
!       else
! 	print_floating (valaddr, type, stream);
        break;
  
      case TYPE_CODE_VOID:
***************
*** 748,796 ****
    fflush (stream);
    return 0;
  }
- 
- #ifdef IEEE_FLOAT
- 
- /* Nonzero if ARG (a double) is a NAN.  */
- 
- int
- is_nan (fp, len)
-      char *fp;
-      int len;
- {
-   int lowhalf, highhalf;
-   union ieee
-     {
-       long i[2];		/* ASSUMED 32 BITS */
-       float f;		/* ASSUMED 32 BITS */
-       double d;		/* ASSUMED 64 BITS */
-     } *arg;
- 
-   arg = (union ieee *)fp;
- 
-   /*
-    * Single precision float.
-    */
-   if (len == sizeof(long))
-     {
-       highhalf = arg->i[0];
-       return ((((highhalf >> 23) & 0xFF) == 0xFF) 
- 	      && 0 != (highhalf & 0x7FFFFF));
-     }
-   
-   /* Separate the high and low words of the double.
-      Distinguish big and little-endian machines.  */
- #ifdef WORDS_BIG_ENDIAN
-     lowhalf = arg->i[1], highhalf = arg->i[0];
- #else
-     lowhalf = arg->i[0], highhalf = arg->i[1];
- #endif
-   
-   /* Nan: exponent is the maximum possible, and fraction is nonzero.  */
-   return (((highhalf>>20) & 0x7ff) == 0x7ff
- 	  && ! ((highhalf & 0xfffff == 0) && (lowhalf == 0)));
- }
- #endif
  
  /* Print a description of a type TYPE
     in the form of a declaration of a variable named VARSTRING.
--- 729,734 ----