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)
thenc < d
for alla,b double
and for allc,d long
Thank you to all of you.
Your requirement is feasible if the following two conditions hold:
sizeof(double)
the same as sizeof(long)
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.
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:
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;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With