5

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

Community
  • 1
  • 1
daTake
  • 87
  • 7
  • I don't know what 'binary String' means, but all you have to do is throw away the last 16 bits and convert the rest to `double`. Not difficult, and all doable within the JDK API. – user207421 Feb 27 '16 at 12:09
  • @ Jean-François Savard - Long.valueOf(hex ,16) takes hex as input ;) so Long.valueOf(hex ,16) and Long#decode same result. – daTake Feb 27 '16 at 12:38
  • 1
    @EJP, you can't just throw away the last 16 bits because the format is different. Exponent is 15 bits in one and 11 bits in the other. – Sergei Tachenov Feb 27 '16 at 12:52
  • 1
    Just throwing away 16 bit doesn't look like a good idea - looking at this topic http://stackoverflow.com/questions/2963055/msvc-win32-convert-extended-precision-float-80-bit-to-double-64-bit there is more to do – daTake Feb 27 '16 at 12:53
  • know a library? the question becomes off-topic, I suggest you [edit] into shape. You can ask help from the answerer if you do not know how – Petter Friberg Feb 27 '16 at 22:50
  • fyi: IEEE 80-bit extended `0x40009E0652141EF0DBF6` is `2.46913578` (exactly). And `0x3FFFC000000000000000` is `1.5` (exactly). – Ian Boyd Jul 28 '22 at 21:02

1 Answers1

5

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.

Sergei Tachenov
  • 24,345
  • 8
  • 57
  • 73
  • The OP might be from a locale that uses a comma as the decimal separator... – Alnitak Feb 27 '16 at 13:52
  • Hello, thanks for the answer - tried your ugly hack with some values and yeah ... your result divided by 2 is the exact value ... – daTake Feb 27 '16 at 14:18
  • 1
    @Alnitak, I'm also from a locale that uses the comma, although I personally prefer the dot. But what does it have to do with the example? It's off by the order of 2, not by the order of thousands. No matter what locale is, one-point-something multiplied by two can't possibly be 1,235. The fact that it's off by 2 means that either the format is wrong or the conversion is wrong. And since C++ prints 2.46..., it means that the format is wrong. – Sergei Tachenov Feb 27 '16 at 14:55
  • @Sergey Tachenov I have an converter decompiled with ida - i will check the values - maybe u are right - BUT it could be possible that the company which developed Open Access 4 obfuscates the values of the .df file. There are no informations out there describing the meta informations in that df file etc. – daTake Feb 27 '16 at 15:14
  • @Sergey thank you your dirty hack is working - yeah you are right the format is not exactly right - but as I said every number (about 500k numbers) divided by two rounded by the HALF_UP on 3 digits is right. – daTake Feb 27 '16 at 17:07
  • 1
    @daTake, note that I screwed up the sign bit (used a wrong mask to extract it). Fixed that now. – Sergei Tachenov Feb 27 '16 at 17:50
  • @Sergey Tachenov the question is now on hold as offtopic - I dont understand what I have to do - can u help me ? – daTake Feb 28 '16 at 19:01
  • @daTake, I don't know exactly. Surely, recommend-a-library type of questions are offtopic here, so you should edit it to make it more than that. Perhaps remove the library thing, make it instead an “I've tried this, but it doesn't work, anyone knows a reliable way to do it?” type of question. – Sergei Tachenov Feb 29 '16 at 04:15
  • Based on @SergeiTachenov 's answer a more generic code would thus be: ``` public static double readExtendedFloat(InputStream is) throws IOException { ByteBuffer bb = ByteBuffer.wrap(is.readNBytes(10)); short high = bb.getShort(); long low = bb.getLong(); long e = (((high & 0x7FFFL) - 0x3FFFL) + 0x3FFL) & 0x7FFL; long ld = ((high & 0x8000L) << 48) | (e << 52) | ((low >>> 11) & 0xF_FFFF_FFFF_FFFFL); return Double.longBitsToDouble(ld); } ``` – Fabien M Jan 17 '23 at 20:38