2010-08-29 3 views
1

longue:problèmes avec la conversion de tableau d'octets à flotter/long en C

char long_num[8]; 
    for(i=0; i<8; i++) 
    long_num[i] = data[pos++]; 

    memcpy(&res, long_num, 8); 

Les valeurs du long_num sont les suivantes:

127 -1 -1 -1 -1 -1 -1 - 1

res doit être la valeur maximale de long signé, mais est plutôt de -129.

EDIT: Celui-ci est pris en charge. C'était le résultat de problèmes de communication: Pour la personne fournissant data, un long est huit octets; pour mon C c'est quatre.

Float:

float *res; 
    /* ... */ 
    char float_num[4]; 
    for(i=0; i<4; i++) 
    float_num[i] = data[pos++]; 

    res = (float *)float_num; 

Il est égal à zéro. Les valeurs de tableau:

62 -1 24 50

J'ai aussi essayé memcpy(), mais il donne zéro aussi bien. Qu'est-ce que je fais mal?


Mon système: Linux 2.6.31-16-generic i686 GNU/Linux

+0

Sur quelle plate-forme travaillez-vous? Ce qui est dans long_num [] avant que la boucle ne soit pas importante; Ce qui est dans les données [] est critique. Vous pouvez utiliser: 'char long_num [8] = {0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};' et vous seriez plus convaincant. De même avec l'exemple float. –

Répondre

2

Voici deux questions, sans aucun rapport.

Dans le premier, votre ordinateur est little-endian. Le bit de signe est défini dans le long que vous avez assemblé pour que le résultat soit négatif. Il est proche de zéro car de nombreux "bits les plus significatifs" sont définis.

Dans le deuxième exemple, le non-respect de strict aliasing rules pourrait être une explication pour un comportement bizarre. Je ne suis pas sûr. Si vous utilisez gcc, essayez d'utiliser une union à la place, gcc garantit ce qui se passe lorsque vous convertissez des données de cette manière en utilisant une union.

3

Vous exécutez le code sur un système little-endian. Inversez l'ordre des octets dans le tableau et réessayez:

signed char long_num[] = {-1, -1, -1, -1, -1, -1, -1, 127}; 
// ... 
1

Vu ce code:

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

int main(void) 
{ 
    { 
     long res; 
     char long_num[8] = { 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; 
     memcpy(&res, long_num, 8); 
     printf("%ld = 0x%lX\n", res, res); 
    } 
    { 
     float res; 

     char float_num[4] = { 62, 0xFF, 24, 50 }; 
     memcpy(&res, float_num, 4); 
     printf("%f = %19.14e\n", res, res); 

    } 
    return 0; 
} 

en mode 64 Compiler bits sur Mac OS X 10.6.4 avec GCC 4.5.1 donne:

-129 = 0xFFFFFFFFFFFFFF7F 
0.000000 = 8.90559981314709e-09 

Ceci est correct pour un Intel little-endian machine (bien, la valeur 'longue' est correcte). Ce que vous essayez de faire est un peu inhabituel - pas recommandé.

Ce n'est pas portable, notamment à cause de problèmes d'endianité.

J'ai déjà écrit un code lié sur une machine SPARC (qui est une machine big-endian):

union u_double 
{ 
    double dbl; 
    char data[sizeof(double)]; 
}; 

union u_float 
{ 
    float flt; 
    char data[sizeof(float)]; 
}; 

static void dump_float(union u_float f) 
{ 
    int exp; 
    long mant; 

    printf("32-bit float: sign: %d, ", (f.data[0] & 0x80) >> 7); 
    exp = ((f.data[0] & 0x7F) << 1) | ((f.data[1] & 0x80) >> 7); 
    printf("expt: %4d (unbiassed %5d), ", exp, exp - 127); 
    mant = ((((f.data[1] & 0x7F) << 8) | (f.data[2] & 0xFF)) << 8) | (f.data[3] & 0xFF); 
    printf("mant: %16ld (0x%06lX)\n", mant, mant); 
} 

static void dump_double(union u_double d) 
{ 
    int exp; 
    long long mant; 

    printf("64-bit float: sign: %d, ", (d.data[0] & 0x80) >> 7); 
    exp = ((d.data[0] & 0x7F) << 4) | ((d.data[1] & 0xF0) >> 4); 
    printf("expt: %4d (unbiassed %5d), ", exp, exp - 1023); 
    mant = ((((d.data[1] & 0x0F) << 8) | (d.data[2] & 0xFF)) << 8) | (d.data[3] & 0xFF); 
    mant = (mant << 32) | ((((((d.data[4] & 0xFF) << 8) | (d.data[5] & 0xFF)) << 8) | (d.data[6] & 0xFF)) << 8) | (d.data[7] & 0xFF); 
    printf("mant: %16lld (0x%013llX)\n", mant, mant); 
} 

static void print_value(double v) 
{ 
    union u_double d; 
    union u_float f; 

    f.flt = v; 
    d.dbl = v; 

    printf("SPARC: float/double of %g\n", v); 
    image_print(stdout, 0, f.data, sizeof(f.data)); 
    image_print(stdout, 0, d.data, sizeof(d.data)); 
    dump_float(f); 
    dump_double(d); 
} 


int main(void) 
{ 
    print_value(+1.0); 
    print_value(+2.0); 
    print_value(+3.0); 
    print_value(0.0); 
    print_value(-3.0); 
    print_value(+3.1415926535897932); 
    print_value(+1e126); 
    return(0); 
} 

C'est ce que je suis arrivé sur cette plate-forme. Notez qu'il y a un bit '1' implicite dans la mantisse, ainsi la valeur de '3' n'a qu'un seul bit défini parce que l'autre 1-bit est impliqué.

SPARC: float/double of 1 
0x0000: 3F 80 00 00          ?... 
0x0000: 3F F0 00 00 00 00 00 00       ?....... 
32-bit float: sign: 0, expt: 127 (unbiassed  0), mant:    0 (0x000000) 
64-bit float: sign: 0, expt: 1023 (unbiassed  0), mant:    0 (0x0000000000000) 
SPARC: float/double of 2 
0x0000: 40 00 00 00          @... 
0x0000: 40 00 00 00 00 00 00 00       @....... 
32-bit float: sign: 0, expt: 128 (unbiassed  1), mant:    0 (0x000000) 
64-bit float: sign: 0, expt: 1024 (unbiassed  1), mant:    0 (0x0000000000000) 
SPARC: float/double of 3 
0x0000: 40 40 00 00          @@.. 
0x0000: 40 08 00 00 00 00 00 00       @....... 
32-bit float: sign: 0, expt: 128 (unbiassed  1), mant:   4194304 (0x400000) 
64-bit float: sign: 0, expt: 1024 (unbiassed  1), mant: 2251799813685248 (0x8000000000000) 
SPARC: float/double of 0 
0x0000: 00 00 00 00          .... 
0x0000: 00 00 00 00 00 00 00 00       ........ 
32-bit float: sign: 0, expt: 0 (unbiassed -127), mant:    0 (0x000000) 
64-bit float: sign: 0, expt: 0 (unbiassed -1023), mant:    0 (0x0000000000000) 
SPARC: float/double of -3 
0x0000: C0 40 00 00          [email protected] 
0x0000: C0 08 00 00 00 00 00 00       ........ 
32-bit float: sign: 1, expt: 128 (unbiassed  1), mant:   4194304 (0x400000) 
64-bit float: sign: 1, expt: 1024 (unbiassed  1), mant: 2251799813685248 (0x8000000000000) 
SPARC: float/double of 3.14159 
0x0000: 40 49 0F DB          @I.. 
0x0000: 40 09 21 FB 54 44 2D 18       @.!.TD-. 
32-bit float: sign: 0, expt: 128 (unbiassed  1), mant:   4788187 (0x490FDB) 
64-bit float: sign: 0, expt: 1024 (unbiassed  1), mant: 2570638124657944 (0x921FB54442D18) 
SPARC: float/double of 1e+126 
0x0000: 7F 80 00 00          .... 
0x0000: 5A 17 A2 EC C4 14 A0 3F       Z......? 
32-bit float: sign: 0, expt: 255 (unbiassed 128), mant:    0 (0x000000) 
64-bit float: sign: 0, expt: 1441 (unbiassed 418), mant:  -1005281217 (0xFFFFFFFFC414A03F) 

Vous auriez à faire quelques diddling au code pour le faire fonctionner sur une machine sanely little-endian comme une machine Intel.

0

Si vous communiquez sur un réseau entre différentes machines (comme l'implique la mise à jour), vous devez définir votre protocole pour vous assurer que les deux extrémités sachent comment obtenir les données avec précision à l'autre extrémité. Ce n'est pas nécessairement trivial - il existe de nombreux systèmes complexes disponibles dans le monde entier.

  • Une méthode standard consiste à définir un ordre canonique pour les octets - et une taille canonique pour les types. Ceci est souvent appelé «ordre des octets réseau» lorsqu'il s'agit d'adresses IPv4, par exemple. Il définit partiellement l'endianness des données; il s'agit également de définir que la valeur est envoyée sous la forme d'une valeur de 4 octets plutôt que d'une valeur de 8 octets - ou vice versa.

  • Une autre technique est basée sur ASN.1 - qui code les données avec un type, une longueur et une valeur (codage TLV). Chaque bit de données est envoyé avec des informations qui identifient ce qui est envoyé.

  • Le protocole DRDA utilisé par IBM DB2 DBMS a une politique différente - 'receiver makes right'. L'expéditeur identifie le type de machine en quelque sorte au démarrage de la session, puis envoie les données dans leur format le plus pratique. Le destinataire est responsable de la réparation de ce qui a été envoyé. (Cela s'applique à la fois au serveur DB et au client DB, le client envoie sa notation préférée et le serveur corrige ce qu'il reçoit, tandis que le serveur envoie sa notation préférée et le client corrige ce qu'il reçoit.)

  • Une autre manière extrêmement efficace de traiter les problèmes consiste à utiliser un protocole textuel. Les données sont transmises en tant que version texte des données, avec un mécanisme clair pour identifier les différents champs. C'est beaucoup plus facile à déboguer que les divers mécanismes de codage binaire parce que vous pouvez vider les données et voir ce qui se passe. Il n'est pas forcément beaucoup moins efficace qu'un protocole binaire - en particulier si vous envoyez généralement des entiers de 8 octets qui contiennent en réalité des valeurs entières à un seul chiffre.

Questions connexes