Разница между прямым присвоением переменной с плавающей запятой шестнадцатеричного целого числа и назначением посредством преобразования указателя - PullRequest
0 голосов
/ 27 апреля 2018

Я исследовал структуру чисел с плавающей запятой и обнаружил, что большинство компиляторов используют стандарт IEEE 754 для хранения чисел с плавающей запятой. И когда я пытался сделать:

float a=0x3f520000; //have to be equal to 0.8203125 in IEEE 754
printf("value of 'a' is: %X [%x] %f\n",(int)a,(int)a, a);

это дает результат:

value of 'a' is: 3F520000 [3f520000] 1062338560.000000

но если я попытаюсь:

int b=0x3f520000;
float* c = (float*)&b;
printf("value of 'c' is: %X [%x] %f\r\n", *(int*)c, *(int*)c, c[0]);

это дает:

value of 'c' is: 3F520000 [3f520000] 0.820313

Вторая попытка дала мне правильный ответ. Что не так с первой попытки? И почему результат отличается от результата, когда я приводил int к float через указатель?

Ответы [ 4 ]

0 голосов
/ 27 апреля 2018

Еще один пример того, как конвертировать во втором случае:

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

int main() {
  uint32_t fi = 0x3f520000;
  float f;
  memcpy(&f, &fi, sizeof(f));
  printf("%.7g\n", f);
}

печатает:

0.8203125

так ты и ожидал.

Подход, который я использовал, это memcpy, который является самым безопасным для всех компиляторов и лучшим выбором для современных компиляторов (GCC начиная с приблизительно 4.6, Clang начиная с 3.x), которые интерпретируют memcpy как "приведение битов" в таком случае и оптимизировать его эффективным и безопасным способом (по крайней мере, в «размещенном» режиме). Это все еще безопасно для старых компиляторов, но не обязательно эффективно таким же образом; некоторые могут предпочесть приведение через объединение или когда-либо через другой тип указателя. Об опасностях, связанных с этим, см. здесь или, как правило, ищите «наказание типа и строгое алиасинг».

(Кроме того, могут существовать некоторые странные платформы, которые страдают от проблемы с порядком байтов, в которой целочисленный порядок байтов отличается от числа с плавающей точкой; байты отличаются от 8 битов и т. Д. Я их здесь не рассматриваю.)

ОБНОВЛЕНИЕ: я начал отвечать на первоначальную версию вопроса. Да, приведение битов и преобразование значений даст принципиально разные результаты. Вот как работают числа с плавающей запятой.

0 голосов
/ 27 апреля 2018

Разница в том, что первое преобразовывает значение (0x3f520000 - это целое число 1062338560) и эквивалентно этому:

float a = 1062338560;
printf("value of 'a' is: %X [%x] %f\n",(int)a,(int)a, a);

Вторая интерпретирует представление int - 111111010100100000000000000000 - как представление float.
(Он также не определен, поэтому не стоит ожидать, что он что-то сделает конкретно).

0 голосов
/ 27 апреля 2018

0x3f520000 - целочисленная константа. При назначении на число с плавающей точкой, целое число преобразуется.

0 голосов
/ 27 апреля 2018

[Примечание: в этом ответе предполагается использование C, а не C ++, для которых применяются другие правила]

С

float a=0x3f520000;

вы берете целое число значение 1062338560 и компилятор преобразует его в 1062338560.0f.

Если вам нужна шестнадцатеричная константа с плавающей точкой , вы должны использовать формат экспоненты, используя букву p. Как в 0x1.a40010c6f7a0bp-1 (который является шестнадцатеричным обозначением для 0.820313).

Что происходит с

int b=0x3f520000;
float* c = (float*)&b;

означает, что вы нарушаете строгий псевдоним и сообщаете компилятору, что c указывает на значение с плавающей точкой (строгий разрыв псевдонима объясняется тем, что b не является значением с плавающей точкой). Затем компилятор переинтерпретирует биты в *c как значение float.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...