tags:

views:

1060

answers:

3

What I really need to do is to export a floating point number to C with no precision loss.

I did this in python:

import math
import struct
x = math.sqrt(2)
print struct.unpack('ii', struct.pack('d', x))
# prints (1719614413, 1073127582)

And in C I try this:

#include <math.h>
#include <stdio.h>

int main(void)
{
  unsigned long long x[2] = {1719614413, 1073127582};
  long long lx;
  double xf;

  lx = (x[0] << 32) | x[1];
  xf = (double)lx;
  printf("%lf\n", xf);
  return 0;
}

But in C I get:

7385687666638364672.000000 and not sqrt(2).

What am I missing?

Thanks.

+4  A: 

The Python code appears to work. The problem is in the C code: you have the long long filled out right, but then you convert the integer value directly into floating point, rather than reinterpreting the bytes as a double. If you throw some pointers/addressing at it it works:

jkugelman$ cat float.c
#include <stdio.h>

int main(void)
{
    unsigned long x[2] = {1719614413, 1073127582};
    double d = *(double *) x;

    printf("%f\n", d);
    return 0;
}
jkugelman$ gcc -o float float.c 
jkugelman$ ./float 
1.414214

Notice also that the format specifier for double (and for float) is %f, not %lf. %lf is for long double.

John Kugelman
I tried with long (before screwing) and I know what happened!I am using 64 bits (AMD64) and sizeof(long) == 8. So I need to use unsigned int here.Thanks!
@Nelson: For portability, you really ought to be using size-defined types, rather than the one that happens to be the right size on your platform - there's no guarantee int will be 32 bits either. The C99 standard defines types like uint32_t (in stdint.h) that will work on all platforms where a double is a standard 64bit IEEE double. On the python side, prefixing the struct string with "=" will always cause it to use fixed sizes (ie "I"=32bit unsigned int), or "<" or ">" to use fixed endianness ("=" uses native endianness, so should match the C compiler).
Brian
+1  A: 

If you're targeting a little-endian architecture,

>>> s = struct.pack('<d', x)
>>> ''.join('%.2x' % ord(c) for c in s)
'cd3b7f669ea0f63f'

if big-endian, use '>d' instead of <d. In either case, this gives you a hex string as you're asking for in the question title's, and of course C code can interpret it; I'm not sure what those two ints have to do with a "hex string".

Alex Martelli
Alex, thanks for your comments. Your answer helps also. What I actually needed was to export to C (generating code). So I can use 0xcd3b7f669ea0f63f (long long in C)...
Well, I prefer to use numbers so that I don't have to care about endianness but it also helps.
You do have to care about endianness -- in what order will you put those two 32-bit numbers?
Alex Martelli
Right :-) Right now I generate things on the same machine and produce an output. But I do have to care..
You're generating C code? Just print the value as a string from Python and let the C compiler store the literal efficiently. This is simpler and faster than the bytes->double conversion in C.
Roger Pate
+2  A: 

repr() is your friend.

C:\junk\es2>type es2.c
#include <stdio.h>
#include <math.h>
#include <assert.h>

int main(int argc, char** argv) {
    double expected, actual;
    int nconv;
    expected = sqrt(2.0);
    printf("expected: %20.17g\n", expected);
    actual = -666.666;
    nconv = scanf("%lf", &actual);
    assert(nconv == 1);
    printf("actual:   %20.17g\n", actual);
    assert(actual == expected);
    return 0;
    }


C:\junk\es2>gcc es2.c

C:\junk\es2>\python26\python -c "import math; print repr(math.sqrt(2.0))" | a
expected:   1.4142135623730951
actual:     1.4142135623730951

C:\junk\es2>
John Machin