Como saída IEEE-754 formato inteiro como um float
-
13-09-2019 - |
Pergunta
Eu tenho um valor inteiro longo não assinado que representa um float usando o formato IEEE-754. Qual é a maneira mais rápida de imprimi-lo como um ponto flutuante em C ++?
Eu sei de uma maneira, mas estou querendo saber se existe um utilitário conveniente em C ++ que seria melhor.
Exemplo da maneira que eu sei é:
union
{
unsigned long ul;
float f;
} u;
u.ul = 1084227584; // in HEX, this is 0x40A00000
cout << "float value is: " << u.f << endl;
(Isso mostra "valor float é: 5")
Solução
O método de união você sugerida é a rota usual de que a maioria das pessoas iria tomar. No entanto, é tecnicamente comportamento indefinido em C / C ++ para ler um membro diferente de uma união do que o que foi escrito mais recentemente. Apesar disso, porém, ele está bem apoiada, entre praticamente todos os compiladores.
Fundição ponteiros, como Jon Skeet sugeriu , é uma má idéia - que viola a regras aliasing estrita de C. Um compilador otimizado agressivo irá produzir código incorreto para isso, uma vez que pressupõe que os ponteiros de tipos unsigned long *
e float *
jamais apelido outro.
A maneira mais correta, em termos de cumprimento de normas (ou seja, não invocando um comportamento indefinido), é fundido por meio de uma char*
, já que as regras aliasing estritas permitir um ponteiro char*
ao apelido um ponteiro de qualquer outro tipo:
unsigned long ul = 0x40A00000;
float f;
char *pul = (char *)&ul; // ok, char* can alias any type
char *pf = (char *)&f; // ok, char* can alias any type
memcpy(pf, pul, sizeof(float));
Embora, honestamente, eu iria apenas com o método de união. A partir do link cellperformance.com acima:
É uma expressão muito comum e é bem suportado por todos os principais compiladores. Como uma questão prática, leitura e escrita a qualquer membro de uma união, em qualquer ordem, é uma prática aceitável.
Outras dicas
Ele só irá funcionar em máquinas de 32 bits ou máquinas onde sizeof (long) == sizeof (float). Em máquinas modernas que você pode querer usar int em vez ... e você realmente tem que ter cuidado se você estiver realizando este truque.
Eu estava pensando a mesma coisa que Jon Skeet, mas ele chegou antes de mim. No entanto, gostaria de fazê-lo um pouco mais concisa do que ele.
cout << "float value is: " << *((float *) &ulValue)) << endl;
Você recebe um ponteiro para o seu unsigned long, então reinterpretar que como um ponteiro para float, então dereferencing o ponteiro para flutuador produz o valor float.
Uma vez que você está fazendo isso em C ++, é mais clara de usar reinterpret_cast em vez do antigo elenco de estilo C.
cout << "float value is: " << *(reinterpret_cast<float *>(&ulValue)) << endl;
EDIT: Eu já pensei que este era "apenas desagradável", mas acaba por ser pior do que eu pensava - ver os comentários para obter detalhes. Eu não recomendo esta abordagem, mas eu estou deixando aqui para documentar a possibilidade (e a ressalva!)
Você pode usar ponteiros:
long ul = 1084227584;
float* fp = (float*) &ul;
float f = *fp;
(Isto pode ser condensada em uma única linha, mas eu acho que é mais claro que não.)
Basicamente a mesma coisa que a sua união ... e igualmente desonesto sem ter a certeza dos tamanhos dos tipos envolvidos.
Se os seus suportes de plataforma-los, você provavelmente deve usar os nomes de tipo específico de tamanho, tanto para o inteiros e flutuantes tipos de ponto.
swegi teve a direção certa, mas perdeu um personagem. A maneira correta é
long l = 1084227584L
float f = reinterpret_cast<float&>(l);