views:

310

answers:

3

Hey,

I need to be able to convert from a Delphi Real48 to C# double.

I've got the bytes I need to convert but am looking for an elegant solution. to the problem.

Anybody out there had to do this before?

I'm needing to do the conversion in C#

Thanks in advance

+2  A: 
static double GetDoubleFromBytes(byte[] bytes)
{
    var real48 = new long[6];
    real48[0] = bytes[0];
    real48[1] = bytes[1];
    real48[2] = bytes[2];
    real48[3] = bytes[3];
    real48[4] = bytes[4];
    real48[5] = bytes[5];

    long sign = (real48[0] & 0x80) >> 7;

    long significand = 
        ((real48[0] % 0x80) << 32) + 
         (real48[1] << 24) + 
         (real48[2] << 16) + 
         (real48[3] << 8) + 
         (real48[4]);

    long exponent = bytes[5];

    if (exponent == 0)
    {
        return 0.0;
    }

    exponent += 894;
    long bits = (sign << 63) + (exponent << 52) + (significand << 13);
    return BitConverter.Int64BitsToDouble(bits);
}
Darin Dimitrov
From Delphi Basics: "Real48:Obsolete - The floating point type with the highest capacity and precision." In modern versions of Delphi that's an Extended (10 Bytes)
JamesB
@Darin, I'm afraid this doesn't seem to be giving the correct answer
mjmcloug
Indeed there seems to be something wrong. I'll check again.
Darin Dimitrov
Thought I'd check SizeOf(Real48) does indeed yield 6.
JamesB
When you assign `bits`, you've re-created the *bit pattern* of a double, but when you pass that to `Convert.ToDouble`, it treats it as an ordinary integer and converts that *integer value* to a double, the same as you'd get from an ordinary long-to-double assignment statement. What you probably want is `BitConverter.ToDouble` instead.
Rob Kennedy
+3  A: 

Ok

I've done some hunting around and I found some C++ code to do the job, converted it and it seems to be giving the right answer... damned if I understand it all though :S

    private static double Real48ToDouble(byte[] real48)
    {

        if (real48[0] == 0)
            return 0.0; // Null exponent = 0

        double exponent = real48[0] - 129.0;
        double mantissa = 0.0;

        for (int i = 1; i < 4; i++)
        {
            mantissa += real48[i];
            mantissa *= 0.00390625; // mantissa /= 256
        }


        mantissa += (real48[5] & 0x7F);
        mantissa *= 0.0078125; // mantissa /= 128
        mantissa += 1.0;

        if ((real48[5] & 0x80) == 0x80) // Sign bit check
            mantissa = -mantissa;

        return mantissa * Math.Pow(2.0, exponent);
    }

If somebody can explain it that would be great :D

mjmcloug
Bytes 1 through 5 represent the fraction part of a number in scientific notation: `1.x * 2^e`. The mantissa is `1.x`. The *for* loop and the two following lines generate *x*. Suppose byte 1 is 0xa5. In binary, that's 10100101. Add that to `mantissa` to get `mantissa == 0xa5`. Then *shift* those bytes down into the fractional part to get the binary value 0.10100101. Shifting 8 is dividing by 256. Repeat for bytes 2 through 4. Byte 5 is special since we only want 7 bits — the eighth bit is the sign bit — so divide by 128 instead. Finally add 1 since that part is *implicit* (not stored anywhere).
Rob Kennedy
Byte 0 is the exponent. It's an unsigned number, but it's biased high by 129, so the first thing to do is correct for that bias. As mentioned in the previous comment, the number is in the form `1.x * 2^e`, where `1.x` is stored in `mantissa` and `e` is stored in `exponent`. The final line of code simply calculates that value as a double.
Rob Kennedy
A: 

I've changed the code you've posted into a more readable format so you can see how it works:

        Double exponentbase = 129d;
        Double exponent = real48[0] - exponentbase; // The exponent is offest so deduct the base.

        // Now Calculate the mantissa
        Double mantissa = 0.0;
        Double value = 1.0;
        // For Each Byte.
        for (int i = 5; i >= 1; i--)
        {
            int startbit = 7;
            if (i == 5)
            { startbit = 6; } //skip the sign bit.

            //For Each Bit
            for (int j = startbit; j >= 0; j--)
            {
                value = value / 2;// Each bit is worth half the next bit but we're going backwards.
                if (((real48[i] >> j) & 1) == 1) //if this bit is set.
                {
                    mantissa += value; // add the value.
                }

            }
        }

        if (mantissa == 1.0 && real48[0] == 0) // Test for null value
            return 0.0;

        if ((real48[5] & 0x80) == 1) // Sign bit check
            mantissa = -mantissa;

        return (1 + mantissa) * Math.Pow(2.0, exponent);
JamesB
This code introduces at least one error. You forgot to test this with negative input.
Rob Kennedy
He was waiting for you to test it for him Rob. Thanks!
Pauk