Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

function to map a double into a long number

Maybe it seems a little bit rare question, but I would like to find a function able to transform a double (c number) into a long (c number). It's not necessary to preserve the double information. The most important thing is:

double a,b;

long c,d;

c = f(a);

d = f(b);

This must be truth:

if (a < b) then c < d for all a,b double and for all c,d long

Thank you to all of you.

like image 372
user2965069 Avatar asked Dec 08 '14 20:12

user2965069


3 Answers

Your requirement is feasible if the following two conditions hold:

  1. The compiler defines sizeof(double) the same as sizeof(long)
  2. The hardware uses IEEE 754 double-precision binary floating-point format

While the 2nd condition holds on every widely-used platform, the 1st condition does not.

If both conditions do hold on your platform, then you can implement the function as follows:

long f(double x)
{
    if (x > 0)
        return double_to_long(x);
    if (x < 0)
        return -double_to_long(-x);
    return 0;
}

You have several different ways to implement the conversion function:

long double_to_long(double x)
{
    long y;
    memcpy(&y,&x,sizeof(x));
    return y;
}

long double_to_long(double x)
{
    long y;
    y = *(long*)&x;
    return y;
}

long double_to_long(double x)
{
    union
    {
        double x;
        long   y;
    }
    u;
    u.x = x;
    return u.y;
}

Please note that the second option is not recommended, because it breaks strict-aliasing rule.

like image 56
barak manos Avatar answered Oct 06 '22 18:10

barak manos


There are four basic transformations from floating-point to integer types:

floor - Rounds towards negative infinity, i.e. next lowest integer.
ceil[ing] - Rounds towards positive infinity, i.e. next highest integer.
trunc[ate] - Rounds towards zero, i.e. strips the floating-point portion and leaves the integer.
round - Rounds towards the nearest integer.

None of these transformations will give the behaviour you specify, but floor will permit the slightly weaker condition (a < b) implies (c <= d).

If a double value uses more space to represent than a long, then there is no mapping that can meet your initial constraint, thanks to the pigeonhole principle. Basically, since the double type can represent many more distinct values than a long type, there is no way to preserve the strict partial order of the < relationship, as multiple double values would be forced to map to the same long value.

See also:

  • Difference between Math.Floor() and Math.Truncate() (Stack Overflow)
  • Pigeonhole principle (Wikipedia)
like image 21
Kenogu Labz Avatar answered Oct 06 '22 19:10

Kenogu Labz


Use frexp() to get you mostly there. It splits the number into exponent and significand (fraction).

Assume long is at least the same size as double, other-wise this is pointless. Pigeonhole principle.

#include <math.h>
long f(double x) {
  assert(sizeof(long) >= sizeof(double));
  #define EXPOWIDTH 11
  #define FRACWIDTH 52
  int ipart;
  double fraction = frexp(fabs(x), &ipart);

  long lg = ipart;
  lg += (1L << EXPOWIDTH)/2;
  if (lg < 0) ipart = 0;
  if (lg >= (1L << EXPOWIDTH)) lg = (1L << EXPOWIDTH) - 1;
  lg <<= FRACWIDTH;

  lg += (long) (fraction * (1L << FRACWIDTH));
  if (x < 0) {
    lg = -lg;
  }
  return lg;
}

-

Notes:

The proper value for EXPO depends on DBL_MAX_EXP and DBL_MIN_EXP and particulars of the double type.

This solution maps the same double values near the extremes of double. I will look and test more later.


Otherwise as commented above: overlay the two types.

As long is often 2's complement and double is laid out in a sign-magnitude fashion, extra work is need when the double is negative. Also watch out for -0.0.

long f(double x) {
  assert(sizeof x == sizeof (long));
  union {
    double d;
    long lg;
  } u = { x*1.0 };  // *1.0 gets rid of -0.0
  // If 2's complement - which is the common situation
  if (u.lg < 0) {
    u.lg = LONG_MAX - u.lg;
  }
  return u.lg;
}
like image 1
chux - Reinstate Monica Avatar answered Oct 06 '22 18:10

chux - Reinstate Monica