Преобразование с плавающей точкой в ​​unsigned int при сохранении порядка - PullRequest
0 голосов
/ 17 сентября 2018

Я нашел много ответов на SO, фокусируясь на преобразовании float в int.

Я манипулирую только положительными значениями с плавающей запятой.Я использовал один простой метод:

unsigned int float2ui(float arg0) {
    float f = arg0;
    unsigned int r = *(unsigned int*)&f;
    return r;
}

Приведенный выше код работает хорошо, но при этом не удается сохранить числовой порядок.Под порядком я имею в виду следующее:

  float f1 ...;
  float f2 ...;
  assert( ( (f1 >= f2) && (float2ui(f1) >= float2ui(f2)) ) ||
          ( (f1 <  f2) && (float2ui(f1) < vfloat2ui(f2)) ));

Я пытался использовать союзы с одинаковыми результатами.Любая идея?Я использую Homebrew gcc 5.3.0.

Ответы [ 3 ]

0 голосов
/ 17 сентября 2018

Код, который вы используете, как написано, имеет неопределенное поведение.Если вы хотите получить доступ к представлению float с частичной переносимостью (определенным реализацией, хорошо определенным при условии, что IEEE 754 и это совпадение с плавающей точкой и целочисленным порядком байтов), вы должны сделать:

uint32_t float2ui(float f){
    uint32_t r;
    memcpy(&r, &f, sizeof r);
    return r;
}

Для не-отрицательные значения, это отображение между значениями с плавающей запятой и представлением сохраняет порядок .Если вы считаете, что не можете сохранить порядок, нам нужно точно определить, какие значения вы считаете контрпримером.

0 голосов
/ 18 сентября 2018

Извлечение битов из числа с плавающей запятой с помощью объединения должно работать.Существует некоторое обсуждение, поддерживает ли стандарт c это.Но что бы ни говорил стандарт, gcc, кажется, поддерживает это.И я ожидаю, что существует слишком много существующего кода, который требует этого, чтобы компиляторы удалили поддержку.

Есть некоторые вещи, о которых вы должны знать, когда помещаете float в int и сохраняете порядок.

  1. Забавные значения, такие как nan, не имеют порядка хранения
  2. floats и хранятся в виде величины и знака, в то время как ints являются комплиментами двух (при условии разумной архитектуры).Таким образом, для отрицательных значений вы должны перевернуть все биты, кроме знакового бита
  3. Если float и int не имеют одинаковых порядковых номеров в вашей архитектуре, вы также должны преобразовать порядковые номера

Вот моя реализация, протестированная с gcc (Gentoo 6.4.0-r1 p1.3) 6.4.0 на x64

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

union ff_t
{
  float f;
  unsigned char a[4];
  int i;
};

int same_endianess = 0;

void
swap_endianess(union ff_t *ff)
{
  if (!same_endianess)
    {
       unsigned char tmp;
       tmp = ff->a[0];
       ff->a[0] = ff->a[3];
       ff->a[3] = tmp;

       tmp = ff->a[1];
       ff->a[1] = ff->a[2];
       ff->a[2] = tmp;
    }
}

void
test_endianess()
{
  union ff_t ff = { ff.f = 1 };

  if (ff.i == 0x3f800000)
    same_endianess = 1;
  else if (ff.i == 0x803f)
    same_endianess = 0;
  else
    {
      fprintf(stderr, "Architecture has some weird endianess");
      exit(1);
    }
}

float
random_float()
{
   float f = random();
   f -= RAND_MAX/2;

   return f;
}

int
f2i(float f)
{
  union ff_t ff = { .f = f };

  swap_endianess(&ff);

  if (ff.i >= 0)
    return ff.i;

  return ff.i ^ 0x3fffffff;
}

float
i2f(int i)
{
  union ff_t ff;
  if (i >= 0)
    ff.i = i;
  else
    ff.i = i ^ 0x3fffffff;

  swap_endianess(&ff);

  return ff.f;
}


int
main()
{
  /* Test if floats and ints uses the same endianess */
  test_endianess();

  for (int n = 0; n < 10000; n++)
    {
       float f1 = random_float();
       int i1 = f2i(f1);
       float f2 = random_float();
       int i2 = f2i(f2);

       printf("\n");
       printf("0x%08x,  %f\n", i1, f1);
       printf("0x%08x,  %f\n", i2, f2);

       assert ( f1 == i2f(i1));
       assert ( f2 == i2f(i2));

       assert ( (f1 <= f2) == (i1 <= i2));
    }
}
0 голосов
/ 17 сентября 2018

Если f1 и f2 являются числами с плавающей запятой, а f1 <= f2, и (int)f1 и (int)f2 являются действительными преобразованиями, то (int)f1 <= (int)f2.

Другими словами, усечение доцелочисленный тип никогда меняет раунд заказа.

Вы можете заменить float2ui просто (int)arg0, проверив, что float находится в границах int.

Обратите внимание, что поведение float до int и float до unsigned равно undefined , если усеченное значение float выходит за пределыдиапазон для типа.

Ваш текущий код, каким-то образом интерпретирующий память float как память int, имеет поведение undefined .Даже ввод символов через union даст вам определенные результаты реализации;обратите внимание, в частности, что sizeof(int) не обязательно совпадает с sizeof(float).

Если , вы используете IEEE754 с одинарной точностью float, 32-битное дополнение 2 int без представления ловушек, с положительным значением для преобразования, последовательным порядком байтов и некоторыми допусками для различных шаблонов, представленных NaN и +-Inf, преобразование, выполняемое типом каламбура , сохраняет порядка.

...