Search code examples
javatype-conversionextended-precision

Convert 80-bit extended precision in java


I'm working on a programm which converts really old Open Access 4 .df Files into other formats and also create Database scripts - I'm already able to convert every possible type except the decimal type. I found out that the byte order has to be a 80-bit extended precision. I allready tried to do the conversion by myself but I was not able to do the conversion from 80-bit extended precision (https://en.wikipedia.org/wiki/Extended_precision#x86_Extended_Precision_Format) to String and String to 80-bit extended precision in Java.

  1. Example:

Value: 1,235

Hex from df File: 40 00 9e 06 52 14 1e f0 db f6

  1. Example:

Value: 0,750

Hex from df File: 3f ff c0 00 00 00 00 00 00 00

Maybe someone can help with the conversion?

Best regards

@EJP comment: Ok I'm throwing away the last 16 bits so I got 3f ff c0 00 00 00 00 00 regarding to (Java - Convert hex to IEEE-754 64-bit float - double precision) I try to convert it with

String hex = "3fffc00000000000";
long longBits = Long.valueOf(hex ,16).longValue();
double doubleValue = Double.longBitsToDouble(longBits);

But the result is 1,984375 and not 0,750


Solution

  • I'm not sure whether it qualifies as an answer or whether it is possible to answer at all, because... Are you sure it's the standard 80-bit floating point format? Because as it is, it looks a bit strange. Take your first example:

    40 00 9e...

    Here, 0x4000−16383=1 is the exponent. The mantissa starts with the MSB 1, which corresponds to the integer part, so it should have a form of 1,... x 2^1, which is greater than 2. But you say it should be 1,235. Doesn't make sense.

    I've created this ugly hack in Java that sort of assembles the double:

    long high = 0x40_00L;
    long low = 0x9e_06_52_14_1e_f0_db_f6L;
    long e = (((high & 0x7FFFL) - 16383) + 1023) & 0x7FFL;
    long ld = ((high & 0x8000L) << 48)
              | (e << 52)
              | ((low >>> 11) & 0xF_FFFF_FFFF_FFFFL);
    System.out.printf("%16X\n", ld);
    double d = Double.longBitsToDouble(ld);
    System.out.println(d);
    

    Assuming the input is correct 80-bit value, this fragment should work fine except it doesn't round properly, doesn't check for overflow, doesn't handle unnormalized values and probably fails to handle special values like NaN. For this input, it prints

    4003C0CA4283DE1B
    2.46913578
    

    Not 1,235! Then I used this C++ code (compiled with GCC, where long double is in the 80-bit format):

    #include <stdio.h>
    
    int main() {
        long long high = 0x4000;
        long long low = 0x9e0652141ef0dbf6;
        long double d;
        char *pd = (char*)&d, *ph = (char*)&high, *pl = (char*)&low;
        for (int i = 0; i < 8; ++i) {
            pd[i] = pl[i];
        }
        for (int i = 0; i < 2; ++i) {
            pd[8 + i] = ph[i];
        }
        printf("%lf\n", (double) d);
    }
    

    It also prints 2.469136.