View mode: basic / threaded / horizontal-split · Log in · Help
March 19, 2009
Re: The Joy of Signalling NaNs! (A compiler patch)
"Mason Green" <mason.green@gmail.com> wrote in message 
news:gpt70t$2b5t$1@digitalmars.com...
> Don wrote:
>> Yup, I've had many NaN nightmares. Whenever you have an uninitialized 
>> variable in C++ there's about a 1% chance that it's a NaN. That's really 
>> fun to track down. NOT.
>> I think it's criminal that this feature has been in hardware since *** 
>> 1980 *** and not made available even in systems languages.
>
> I second adding this feature to D.  I've spent many, many hours chasing 
> NaN bugs when porting box2d from c++.  Most of the time, it came down to 
> simply forgetting to initialize a float somewhere. I actually make a post 
> on dsource a few days ago:
>
> http://www.dsource.org/forums/viewtopic.php?t=4501&sid=2e8f71e52efbcece43a63cbb261e8706
>

Warnings or errors on the use of uninitialized variables would also solve 
that problem *wink* *wink* *nudge* *nudge*.
March 19, 2009
Re: The Joy of Signalling NaNs! (A compiler patch)
Walter Bright wrote:
> Georg Wrede wrote:
>> Thanks, Don and Walter!!
> 
> I originally had this in the Digital Mars C compiler years and years 
> ago. I dropped it and moved away from it because not a single person 
> noticed or cared about it, plus the standard C spec failed to 
> distinguish between quiet and signaling nan's.
> 
> I'm glad to see it actually being of value, and it's a great idea.

I noticed there was a place in the backend where it was careful to 
preserve the signallingness <g> of NaNs. So I thought, hmm, someone's 
been here before.

The key thing that makes it possible is that D initializes all floats to 
NaN anyway, and since C doesn't do that, it's not as obviously 
beneficial. By making NaN the default, you've made sure that practically 
every D user knows about them. I bet that's not the case for C/C++. (I 
even know numerical analyists who don't know much about them).
March 19, 2009
Re: The Joy of Signalling NaNs! (A compiler patch)
Don wrote:
> Walter Bright wrote:
>> Georg Wrede wrote:
>>> Thanks, Don and Walter!!
>>
>> I originally had this in the Digital Mars C compiler years and years 
>> ago. I dropped it and moved away from it because not a single person 
>> noticed or cared about it, plus the standard C spec failed to 
>> distinguish between quiet and signaling nan's.
>>
>> I'm glad to see it actually being of value, and it's a great idea.
> 
> I noticed there was a place in the backend where it was careful to 
> preserve the signallingness <g> of NaNs. So I thought, hmm, someone's 
> been here before.

Like a chicken that still has genes for dinosaur teeth lurking unactivated!

> 
> The key thing that makes it possible is that D initializes all floats to 
> NaN anyway, and since C doesn't do that, it's not as obviously 
> beneficial. By making NaN the default, you've made sure that practically 
> every D user knows about them. I bet that's not the case for C/C++. (I 
> even know numerical analyists who don't know much about them).

You can still know it's not the case for C/C++ given the crappy support 
for nan in modern compilers.
March 20, 2009
Re: The Joy of Signalling NaNs! (A compiler patch)
Great work Don!
Fawzi

On 2009-03-19 05:58:40 +0100, Don <nospam@nospam.com> said:

> It's great that D initializes floating-point variables to NaN, instead 
> of whatever random garbage happened to be in RAM.
> But, if your calculation ends up with a NaN, you have to work out where 
> it came from. Worse, the NaN might not necessarily
> be visible in your final results, but you results may nonetheless be wrong.
> 
> The hardware has excellent support for debugging these problems -- all 
> you need to do is activitating the floating-point 'invalid' trap,
> and you'll get a hardware exception whenever you _create_ a NaN. What 
> about uninitialized variables?
> Signalling NaNs are designed for exactly this situation. The instant 
> you access a signalling NaN, a hardware exception occurs,
> and you drop straight into your debugger (just like accessing a null pointer).
> But this could only work if the compiler initialized every 
> uninitialised floating-point variable to a signalling NaN.
> 
> Now that we have access to the backend (thanks Walter!), we can do 
> exactly that. My patch(below) is enabled only when compiled with DMC.
> real.nan is unchanged, and won't cause exceptions if you use it, but 
> real.init is now a signalling nan.
> This doesn't make any difference to anything, until you enable FP exceptions.
> And when you do, if no exceptions occur, you can use the code coverage 
> feature to give very high confidence that you're not using any 
> uninitialised floating-point variables.
> 
> I propose that this should become part of DMD. It doesn't need to be in 
> the spec, it's primarily for debugging.
> 
> Don.
> 
> ==============
> Example of usage:
> ==============
> 
> void main()
> {
>      double a, b, c;
>      a*=7;        // Exactly the same as it is now, a is nan.
> 
>      enableExceptions();
> 
>      c = 6;      // ok, c is initialized now
>      c *= 10;
>      b *= 10;    // BANG ! Straight into the debugger
>      b *= 5;
> 
>      disableExceptions();
> }
> 
> -------
> 
> void enableExceptions() {
>      version(D_InlineAsm_X86) {
>           short cont;
>           asm {
>               fclex;
>               fstcw cont;
>               mov AX, cont;
>               and AX, 0xFFFE; // enable invalid exception
>               mov cont, AX;
>               fldcw cont;
>           }
>       }
>   }
> 
> void disableExceptions() {
>      version(D_InlineAsm_X86) {
>           short cont;
>           asm {
>               fclex;
>               fstcw cont;
>               mov AX, cont;
>               or AX, 0x1; // disable invalid exception
>               mov cont, AX;
>               fldcw cont;
>           }
>       }
>   }
> 
> 
> =================================
> Patches to DMD to turn all unitialized floats into SNANs.
> 
> Changes are in mytype.c and e2ir.c
> =================================
> mytype.c:
> =================================
> line 2150:
> 
> Expression *TypeBasic::defaultInit(Loc loc)
> {   integer_t value = 0;
> #if __DMC__
> 	// Note: could be up to 16 bytes long.
> 	unsigned short snan[8] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xBFFF, 0x7FFF, 0, 0, 0 };
> 	d_float80 fvalue = *(long double*)snan;
> #endif
> 
> line 2177:
> 
> 	case Tfloat80:
> #if __DMC__
> 		return new RealExp(loc, fvalue, this);
> #else
> 	    return getProperty(loc, Id::nan);
> #endif
> 
> line 2186:
> 
> 	case Tcomplex80:
> #if __DMC__
> 		{   // Can't use fvalue + I*fvalue (the im part becomes a quiet NaN).
> 			complex_t cvalue;
> 			((real_t *)&cvalue)[0] = fvalue;
> 			((real_t *)&cvalue)[1] = fvalue;
> 			return new ComplexExp(loc, cvalue, this);
> 		}
> #else
> 	    return getProperty(loc, Id::nan);
> #endif
> =================================
> e2ir.c line 1182.
> =================================
> 
> bool isSignallingNaN(real_t x)
> {
> #if __DMC__
> 	if (x>=0 || x<0) return false;
> 	return !((((unsigned short*)&x)[3])&0x4000);
> #else
> 	return false;
> #endif
> }
> 
> 
> elem *RealExp::toElem(IRState *irs)
> {   union eve c;
>      tym_t ty;
> 
>      //printf("RealExp::toElem(%p)\n", this);
>      memset(&c, 0, sizeof(c));
>      ty = type->toBasetype()->totym();
>      switch (tybasic(ty))
>      {
> 	case TYfloat:
> 	case TYifloat:
> 	    c.Vfloat = value;
> 		if (isSignallingNaN(value) ) {
> 			((unsigned int*)&c.Vfloat)[0] &= 0xFFBFFFFFL;
> 		}
> 	    break;
> 
> 	case TYdouble:
> 	case TYidouble:
> 		c.Vdouble = value; // this unfortunately converts SNAN to QNAN.
> 		if ( isSignallingNaN(value) ) {
> 			((unsigned int*)&c.Vdouble)[1] &= 0xFFF7FFFFL;
> 		}
> 	    break;
> 
> 	case TYldouble:
> 	case TYildouble:
> 	    c.Vldouble = value;
> 	    break;
> 
> 	default:
> 	    print();
> 	    type->print();
> 	    type->toBasetype()->print();
> 	    printf("ty = %d, tym = %x\n", type->ty, ty);
> 	    assert(0);
>      }
>      return el_const(ty, &c);
> }
> 
> elem *ComplexExp::toElem(IRState *irs)
> {   union eve c;
>      tym_t ty;
>      real_t re;
>      real_t im;
> 
>      re = creall(value);
>      im = cimagl(value);
> 
>      memset(&c, 0, sizeof(c));
>      ty = type->totym();
>      switch (tybasic(ty))
>      {
> 	case TYcfloat:
> 	    c.Vcfloat.re = (float) re;
> 	    c.Vcfloat.im = (float) im;
> 		if ( isSignallingNaN(re) && isSignallingNaN(im)) {
> 			((unsigned int*)&c.Vcfloat.re)[0] &= 0xFFBFFFFFL;
> 			((unsigned int*)&c.Vcfloat.im)[0] &= 0xFFBFFFFFL;
> 		}
> 	    break;
> 
> 	case TYcdouble:
> 	    c.Vcdouble.re = (double) re;
> 	    c.Vcdouble.im = (double) im;
> 		if ( isSignallingNaN(re) && isSignallingNaN(im)) {
> 			((unsigned int*)&c.Vcdouble.re)[1] &= 0xFFF7FFFFL;
> 			((unsigned int*)&c.Vcdouble.im)[1] &= 0xFFF7FFFFL;
> 		}
> 	    break;
> 
> 	case TYcldouble:
> 	    c.Vcldouble.re = re;
> 	    c.Vcldouble.im = im;
> 	    break;
> 
> 	default:
> 	    assert(0);
>      }
>      return el_const(ty, &c);
> }
Next ›   Last »
1 2
Top | Discussion index | About this forum | D home