Arucard1983
Power Member
Vou partilhar um snipet de código em C/C++ que permite transformar um array char de 8 bytes, como acontece na leitura de ficheiros binários, num número de vírgula flutuante padrão.
- Neste código, assume-se que array representa um número no formato big-endian, como acontece em muitos ficheiros de dados binários. (No meu projecto de doutoramento em Física Nuclear, acabei por ter a necessidade de praticar engenharia reversa aos ficheiros do software LabView, e muitos dos dados representam números de vírgula flutuante no formato hexadecimal... )
- Mas a maioria dos processadores utilizam o formato little-endian, e após experimentar uns cinco métodos que falharam todos de forma gravosa, ou davam NaN, ou davam um número fora do expectável, acabei por implementar um algoritmo, portável, e ainda é endianess-safe !
Para funcionar, devem aplicar a função de leitura preferida (fopen) com 8 bytes, e exportar para um array unsigned char de 9 bytes (por causa do símbolo '\0'), depois apliquem a função conforme descrita.
Com um pequeno aperfeiçoamento, podem meter um segundo argumento para indicar a endianess do argumento, e desta forma obtêm-se um double sem grandes problemas...
......................................
Como resolviam este problema ?
- Neste código, assume-se que array representa um número no formato big-endian, como acontece em muitos ficheiros de dados binários. (No meu projecto de doutoramento em Física Nuclear, acabei por ter a necessidade de praticar engenharia reversa aos ficheiros do software LabView, e muitos dos dados representam números de vírgula flutuante no formato hexadecimal... )
- Mas a maioria dos processadores utilizam o formato little-endian, e após experimentar uns cinco métodos que falharam todos de forma gravosa, ou davam NaN, ou davam um número fora do expectável, acabei por implementar um algoritmo, portável, e ainda é endianess-safe !
Código:
double MyUtils::CharToDouble(unsigned char* x)
{
// The IEEE-754 double float number on hexadecimal format requires some delicate tasks, since it will follow the pattern 1-byte*11-byte*52-byte
unsigned int x7,x6,x5,x4,x3,x2,x1,x0,x1e,x1m,exp;
double z,zd,sign,expf;
x0 = x[0];
x1 = x[1];
x2 = x[2];
x3 = x[3];
x4 = x[4];
x5 = x[5];
x6 = x[6];
x7 = x[7];
// Get the number sign, and fix the header
if(x0 >= 128)
{
x0 = x0 - 128;
sign = -1.0;
}
else
{
sign = 1.0;
}
// Split the second byte, since the first half belongs to the exponential, and the second half to the mantissa.
x1e = x1 / 16;
x1m = x1 % 16;
// Evaluate the exponential part. Note: It is necessary to split the integer and fractional conversion to avoid mistakes!
exp = x0*16+x1e;
expf = 1.0*exp - 1023.0;
// Evaluate the mantissa
zd = 1 + x1m*std::pow(2,-4) + x2*std::pow(2,-12) + x3*std::pow(2,-20) + x4*std::pow(2,-28) + x5*std::pow(2,-36) + x6*std::pow(2,-44) + x7*std::pow(2,-52);
// Evaluate the final result:
z = sign * std::pow(2,expf) * zd;
return z;
}
Para funcionar, devem aplicar a função de leitura preferida (fopen) com 8 bytes, e exportar para um array unsigned char de 9 bytes (por causa do símbolo '\0'), depois apliquem a função conforme descrita.
Com um pequeno aperfeiçoamento, podem meter um segundo argumento para indicar a endianess do argumento, e desta forma obtêm-se um double sem grandes problemas...
......................................
Como resolviam este problema ?