4

Quiero convertir un string de 6 bytes en hexadecimal a un float

   float desencapsularArchivo::hex2dec(string aConvertir)
    {
    int cantHex = aConvertir.size();

    float decimal = 0;

    for(int hexNum = 0;hexNum < cantHex; hexNum++)
    {
        char piv = aConvertir.at(hexNum);

        float hex;

        switch(piv)
        {
        case 'A': case 'a':
            hex = 10;
            break;
        case 'B': case 'b':
            hex = 11;
            break;
        case 'C': case 'c':
            hex = 12;
            break;
        case 'D': case 'd':
            hex = 13;
            break;
        case 'E': case 'e':
            hex = 14;
            break;
        case 'F': case 'f':
            hex = 15;
            break;
        default:
            hex = (int)piv - 48;
        }
        decimal = decimal + hex * pow(16, cantHex - hexNum - 1);
    }

    return decimal;

}

Si quiero introducir por ejemplo la cadena 00805f181015 el resultado es: 551351222272 en vez de: 551351226389

Segun mis pruebas faltan sumas los ultimos 4 caracteres del hexadecimal, pero no se por que no lo hace.

Cabe mencionar que para imprimir uso cout << "Decimal: " << setprecision(0) << fixed << decimal;

Yoel Rodriguez
  • 1,733
  • 13
  • 26
codeKiller
  • 41
  • 4
  • 1
    https://es.stackoverflow.com/questions/197/por-qu%c3%a9-mis-programas-no-pueden-hacer-c%c3%a1lculos-aritm%c3%a9ticos-correctamente – Trauma Feb 21 '18 at 18:59
  • ¿Por qué `float` en lugar de `uint64_t`*? Digo, es un hexstring lo que tienes ... – NaCl Feb 21 '18 at 19:13
  • Intente utilizar uint64_t y no me funcionaba o no supe implementarlo, me seguia marcanod numeros negatimos como un int de 32 y ocupo que sea hasta un minimo de 48 bits, por eso uso float – codeKiller Feb 21 '18 at 19:16
  • Entonces usa `unsigned long long` o `uint64_t`. `float` es igual de 32 bits. – NaCl Feb 21 '18 at 19:17
  • Si quieres convertir un string de 6 bytes en un número necesitarás uno que admita 6 bytes... `uint64_t` ocupa 8 luego debería ser más que suficiente... otra cosa es que el algoritmo te falle por otra razón – eferion Feb 22 '18 at 08:50

2 Answers2

3

Tu algoritmo es correcto; los tipos usados son incorrectos. Estás teniendo un problema de pérdida de precisión al usar números en coma flotante. Como ya se mencionó en otra pregunta, no todos los números decimales son representables con el formato de coma flotante IEEE.

En concreto el número 551351226389 no tiene representación precisa en coma flotante, así que el valor que se aproxima es 5513512222721, puedes comprobarlo en esta página web. Si cambias todos tus float de tu código por tipos enteros (long o long long), el error desaparece.


  1. Una pérdida de precisión del 0,000001%.
PaperBirdMaster
  • 44,474
  • 6
  • 44
  • 82
2

Si quieres convertir un string de 6 bytes en un número necesitarás uno que admita 6 bytes... uint64_t ocupa 8 luego debería ser más que suficiente... otra cosa es que el algoritmo te falle por otra razón .

Y para muestra, un botón. El siguiente ejemplo convierte perfectamente, entre otros, el número problemático (00805f181015):

unsigned long long Convertir(std::string const& cadena)
{
  unsigned long long numero = 0;

  for( char c : cadena )
  {
    numero *= 16;

    if( c >= 'a' )
      numero += c - 'a' + 10;
    else
      numero += c - '0';
  }

  return numero;
}

int main()
{
  std::cout << Convertir("1") << '\n'
            << Convertir("10b") << '\n'
            << Convertir("123456") << '\n'
            << Convertir("999998") << '\n'
            << Convertir("00805f181015") << '\n';
}

PD.: Uso unsigned long long por no cargar librerías adicionales, pero el efecto es el mismo que al usar uint64_t

eferion
  • 49,291
  • 5
  • 30
  • 72