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")

Foi útil?

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);
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top