liv2hak
liv2hak

Reputation: 15010

high precision division in C

I want to do the following calculation without losing precision in C.

uint64_t ts_1 = 0x5212cb03ca115ac0; 
uint64_t ts_2 = 0x5212cb03ca115cc0; 
uint64_t ts_delta = (ts_2 - ts_1)

double scale_factor = ts_delta/(2^32)

I am getting the value of ts_delta as 0x200. However the value of scale_factor as 15.000000.Basically I am losing precision during the calculation.

How do I do it without losing precision.?

Here is a short self contained example on how I am trying to print.

#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>

int main()
{
        uint64_t ts_1 = 0x5212cb03ca115ac0;
        uint64_t ts_2 = 0x5212cb03ca115cc0;
        uint64_t ts_delta = (ts_2 - ts_1);
        double scale_factor = ((double)ts_delta) / (((uint64_t)1) << 32);

        printf("ts_delta %"PRIx64" scale factor %f \n",ts_delta,scale_factor);

        return 0;
}

Upvotes: 0

Views: 2938

Answers (2)

zwol
zwol

Reputation: 140846

You're not doing the calculation you think you're doing. The ^ operator in C does not perform exponentiation, it performs bitwise exclusive OR. You are actually dividing ts_delta by 2 xor 32 == 34. I'm not sure how you got 15.000000 from that, it should have come out 15.058823529411764.

The calculation you wanted to do is expressed in C like this:

double scale_factor = ((double)ts_delta) / (((uint64_t)1) << 32);

EDIT: From the other answer: if your compiler supports C99's hexadecimal floating-point literals, you can also write

double scale_factor = ts_delta * 0x1p-32;

Written this way, no casts are necessary, because one side of the multiplication is already a double, so the integer on the other side will be converted to match. Unfortunately, several compilers have uint64_t but no other features of C99, e.g. recent MSVC.

Upvotes: 3

Kerrek SB
Kerrek SB

Reputation: 477560

You need to compute floating point values, and get the expression for raising to a power right. You can use the standard function ldexp:

#include <math.h>

double scale_factor = ldexp(ts_2 - ts_1, -32)

Or just multiply by a constant (thanks @Eric Postpischil!):

double scale_factor = (ts_2 - ts_1) * 0x1p-32;

Upvotes: 2

Related Questions