Функция для отображения двойного в длинное число

Может быть, это немного редкий вопрос, но я хотел бы найти функцию, способную преобразовать double (с число) в long (с номером). Нет необходимости сохранять двойную информацию. Самое главное это:

double a,b;

long c,d;

c = f(a);

d = f(b);

Это должно быть правдой

если (a < b) затем c < d для всех a,b double и для всех c,d long

Спасибо всем вам.

3 ответа

Ваше требование выполнимо, если выполняются следующие два условия:

  1. Компилятор определяет sizeof(double) такой же как sizeof(long)
  2. Аппаратное обеспечение использует двоичный формат IEEE 754 с двойной точностью

В то время как 2-е условие выполняется на каждой широко используемой платформе, 1-е условие не выполняется.

Если на вашей платформе выполняются оба условия, вы можете реализовать эту функцию следующим образом:

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

У вас есть несколько разных способов реализовать функцию преобразования:

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;
}

Обратите внимание, что второй вариант не рекомендуется, поскольку он нарушает правило строгого наложения имен.

Использование frexp() чтобы вы были в основном там. Он делит число на экспоненту и значимость (дробь).

Предполагать long по крайней мере такой же размер, как doubleв остальном это бессмысленно. Принцип голубиных отверстий.

#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;
}

-

Заметки:

Подходящее значение для EXPO зависит от DBL_MAX_EXP а также DBL_MIN_EXP и подробности double тип.

Это решение отображает то же самое double значения, близкие к крайним double, Я посмотрю и опробую больше позже.


В противном случае, как указано выше: наложение двух типов.

Как long часто дополняют 2 и double выложен в значительном размере, дополнительная работа необходима, когда double отрицательно. Также следите за -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;
}

Существует четыре основных преобразования из типов с плавающей точкой в ​​целочисленные:

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.

Ни одно из этих преобразований не даст указанное вами поведение, но floor позволит немного более слабое состояние (a < b) implies (c <= d),

Если double значение использует больше места для представления, чем longтогда нет никакого сопоставления, которое могло бы соответствовать вашему начальному ограничению, благодаря принципу голубиных отверстий. В основном, так как double Тип может представлять гораздо больше различных значений, чем long типа, нет никакого способа сохранить строгий частичный порядок < отношения, как множественные double значения будут вынуждены сопоставить с тем же long значение.

Смотрите также:

Другие вопросы по тегам