Testing for a float NaN results in a stack overflow

前端 未结 3 867
广开言路
广开言路 2020-12-31 11:40

C#, VS 2010

I need to determine if a float value is NaN.

Testing a float for NaN using

float.IsNaN(aFloatNumber) 

crashes

3条回答
  •  没有蜡笔的小新
    2020-12-31 11:45

    works OK in the constructor of theclass for a custom control

    This is the only real hint towards the underlying problem. Code that runs on a thread can manipulate two stacks inside the processor. One is the normal one that everybody knows about and gave this web site its name. There is however another one, well hidden inside the FPU (Floating Point Unit). It stores intermediate operand values while making floating point calculations. It is 8 levels deep.

    Any kind of mishap inside the FPU is not supposed to generate runtime exceptions. The CLR assumes that the FPU is configured with its defaults for the FPU control word, the hardware exceptions it can generate are supposed to be disabled.

    That does have a knack for going wrong when your program uses code that came from the 1990s, back when enabling FPU exceptions still sounded like a good idea. Code generated by Borland tooling are notorious for doing this for example. Its C runtime module reprograms the FPU control word and unmasks the hardware exceptions. The kind of exceptions you can get for that can be very mysterious, using NaN in your code is a good way to trigger such an exception.

    This should be at least partially visible with the debugger. Set a breakpoint on the "still good" code and use the Debug + Windows + Registers debugger window. Right-click it and select "Floating point". You'll see all of the registers that are involved with floating point calculations, ST0 through ST7 are the stack registers for example. The important one here is marked CTRL, its normal value in a .NET process is 027F. The last 6 bits in that value are the exception masking bits (0x3F), all turned on to prevent hardware exceptions.

    Single step through the code and the expectation is that you see the CTRL value change. As soon as it does then you'll have found the evil code. If you enable unmanaged debugging then you should also see the load notification in the Output window and see it appear in the Debug + Windows + Module window.

    Undoing the damage that the DLL did is fairly awkward. You'd have to pinvoke _control87() in msvcrt.dll for example to restore the CTRL word. Or a simple trick that you can use, you can intentionally throw an exception. The exception handling logic inside the CLR resets the FPU control word. So with some luck, this kind of code is going to solve your problem:

        InitializeComponent();
        try { throw new Exception("Please ignore, resetting FPU"); }
        catch {}
    

    You may have to move it, next best guess is the Load event. The debugger should tell you where.

提交回复
热议问题