1

I'm running into an issue with certain double to unsigned __int64 typecasts in C++ Builder 10.1 Berlin throwing a floating point invalid operation exception($C0000090).

My application allows for user input, so I need to be able to handle any values, but for simplicity, let's use the following example:

double dValue = 9223372036854775807.0;
unsigned __int64 uhValue = (unsigned __int64) dValue;

While I can handle this exception in a try/catch, this appears to corrupt the FPU register stack, and subsequent floating point operations will generate a floating point stack check exception.

The following problem report describes a similar issue and has a potential workaround: http://qc.embarcadero.com/wc/qcmain.aspx/qcmain.aspx?d=119745

I'd like to prevent the initial floating point exception if at all possible. Precision loss aside, the value stored in the double is within the valid unsigned __int64 range, so I'm having trouble understanding what's causing the exception.

Is there something I could be doing differently when it comes to the typecast/conversion or exception handling?

As a side note, the code snippet above runs fine in Visual Studio and sets uhValue to 9223372036854775808.

Thank you!

Nenad
  • 13
  • 2
  • [Quality Central](http://qc.embarcadero.com) is the old bug reporting system, which has been replaced with [Quality Portal](http://quality.embarcadero.com). I don't see any existing reports that look like this issue, so feel free to create a new bug report (and reference the old bug). – Remy Lebeau Mar 24 '17 at 02:15

1 Answers1

0

I use only old BDS2006 so I can not test your conditions. But you can do the loading your self ... something like this:

The pure 64 bit version would look like this:

#pragma warn -8056
unsigned __int64 load(double x) // MSb sign(1),exp(11),1.0+mant(52) LSb
{
    int exp;
    unsigned __int64 y, *q = ((unsigned __int64*)(&x));

    exp = (*q >> 52) & 0x7FF; // extract exponent
    if (exp == 0x7FF)
        return 0; // here handle +/- inf value
    else
    { // extract mantissa and debiass exponent
        if (exp == 0x00) // denormalized form 0.000001
        {
            exp = -1024;
            y = *q & 0x000FFFFFFFFFFFFF;
        }
        else
        { // normalized form 1.000001
            exp -= 1023;
            y = *q & 0x000FFFFFFFFFFFFF;
            y |= 0x0010000000000000;
        }
    }
    // update your 64bit value with exponent shift and sign (this part is untested)
    exp -= 52;
    if (exp > 0)
        y <<= +exp;
    if (exp < 0)
        y >>= -exp;
    if (*q >= 0x8000000000000000)
        y = -y; // handle sign
    return y;
}
#pragma warn .8056

Hope I did not do some silly thing as I can not test it (my compiler does not support 64 bit arithmetic) the code was taken from my arbnum class double loader and change to suit your needs.

BTW I am not entirely convinced the cast is the problem I fight with similar problems in my projects before. I found two main reasons producing these on them own:

  1. using OpenGL where pairs glBegin()/glEnd() was not properly balanced.

    Missing glEnd() was throwing FPU stack exceptions all the time in unrelated code to it ...

  2. BCB6 inherited Borland C++ engine and compiler bug.

    I do not use BCB6 (I am on BDS2006) but you guys confirmed it is present there too (BCB5 was fine) and possibly also in newer versions of Borland also. Simply default constructor generated by compiler is bugged unleashing havoc to compiler and C++ engine. In combination with memory manager bug (double delete of pointer does not throw exception but invalidates memory manager instead) you can invalidate pointers on any time unknowingly overwrite even parts of code in memory... not to mention occasional exceptions of any kind (most probable is access violation) but I got the same FPU crap like you for some projects before I got at the bottom of this thing (btw you can use single fninit instruction to restore FPU time to time as a last resort).

    For more info and how to solve it see:

Community
  • 1
  • 1
Spektre
  • 49,595
  • 11
  • 110
  • 380
  • I applied a minor set of edits to the code, as I could not compile without errors, and when I fixed the errors I got warnings. The changes remedy those and now the input `double` of 9223372036854775807.0 is returned as an unsigned `__int64` of 9223372036854775808 – blong Mar 28 '17 at 14:31
  • @blong yep different compiler sometimes lead to different code ... (I approved your edit). btw may be was enough to change `unsigned __int64 y, *q = (((unsigned __int64)*)(&x));` or use `typedef` for `unsigned __int64` as the problem is it is 2 words instead of 1 – Spektre Mar 28 '17 at 17:41