45 votes

Fonction "non sécurisée" en C# - *(float*)(&result) vs. (float)(result)

Quelqu'un peut-il expliquer de manière simple les codes ci-dessous :

public unsafe static float sample(){    
      int result = 154 + (153 << 8) + (25 << 16) + (64 << 24);

      return *(float*)(&result); //don't know what for... please explain
}

Nota: le code ci-dessus utilise non sécurisé fonction

Pour le code ci-dessus, j'ai du mal car je ne comprends pas quelle est la différence entre sa valeur de retour et la valeur de retour ci-dessous :

return (float)(result);

Est-il nécessaire d'utiliser une fonction non sécurisée si vous retournez *(float*)(&result) ?

75voto

Martin Liversage Points 43712

Sur .NET, un float est représentée par un IEEE binaire32 nombre flottant de précision simple stocké en utilisant 32 bits. Apparemment, le code construit ce nombre en assemblant les bits dans un fichier de type int et le convertit ensuite en un float en utilisant unsafe . Le cast est ce que l'on appelle en C++ un reinterpret_cast où aucune conversion n'est effectuée lorsque le transfert est réalisé - les bits sont simplement réinterprétés comme un nouveau type.

IEEE single precision floating number

Le numéro assemblé est 4019999A en hexadécimal ou 01000000 00011001 10011001 10011010 en binaire :

  • Le bit de signe est 0 (c'est un nombre positif).
  • Les bits de l'exposant sont 10000000 (ou 128), ce qui donne l'exposant 128 - 127 = 1 (la fraction est multipliée par 2^1 = 2).
  • Les bits de fraction sont 00110011001100110011010 qui, à défaut d'autre chose, ont presque un modèle reconnaissable de zéros et de uns.

Le flottant retourné a exactement les mêmes bits que 2.4 converti en virgule flottante et la fonction entière peut simplement être remplacée par le littéral 2.4f .

Le zéro final qui "casse le schéma binaire" de la fraction est peut-être là pour faire correspondre le flottant à quelque chose qui peut être écrit en utilisant un littéral à virgule flottante ?


Alors quelle est la différence entre un plâtre normal et ce plâtre bizarre "non sécurisé" ?

Supposons le code suivant :

int result = 0x4019999A // 1075419546
float normalCast = (float) result;
float unsafeCast = *(float*) &result; // Only possible in an unsafe context

La première distribution prend l'entier 1075419546 et le convertit en sa représentation en virgule flottante, par exemple 1075419546f . Cela implique de calculer les bits de signe, d'exposant et de fraction nécessaires pour représenter le nombre entier d'origine sous forme de nombre à virgule flottante. Il s'agit d'un calcul non trivial qui doit être effectué.

La deuxième distribution est plus sinistre (et ne peut être exécutée que dans un contexte non sécurisé). L'adresse &result prend l'adresse de result renvoyant un pointeur vers l'emplacement où l'entier 1075419546 est stocké. L'opérateur de déréférencement de pointeur * peut alors être utilisé pour récupérer la valeur pointée par le pointeur. Utilisation de *&result récupérera le nombre entier stocké à l'emplacement cependant en coulant d'abord le pointeur vers un fichier float* (un pointeur vers un float ), un flotteur est plutôt extrait de l'emplacement mémoire, ce qui donne le flotteur 2.4f étant affecté à unsafeCast . Donc le récit de *(float*) &result es donnez-moi un pointeur sur result et supposer que le pointeur est un pointeur vers un float et récupère la valeur pointée par le pointeur .

Contrairement au premier lancer, le deuxième lancer ne nécessite aucun calcul. Elle envoie simplement le 32 bits stocké dans result en unsafeCast (qui, heureusement, est aussi en 32 bits).

En général, l'exécution d'un tel lancer peut échouer de plusieurs façons mais en utilisant unsafe vous dites au compilateur que vous savez ce que vous faites.

19voto

Bradley Smith Points 6285

Si j'interprète correctement ce que fait la méthode, c'est un équivalent sûr :

public static float sample() {    
   int result = 154 + (153 << 8) + (25 << 16) + (64 << 24);

   byte[] data = BitConverter.GetBytes(result);
   return BitConverter.ToSingle(data, 0);
}

Comme on l'a déjà dit, il s'agit de réinterpréter la int comme une valeur float .

3voto

Rune Grimstad Points 17775

Cela ressemble à une tentative d'optimisation. Au lieu d'effectuer des calculs en virgule flottante, vous effectuez des calculs entiers sur la représentation entière d'un nombre en virgule flottante.

N'oubliez pas que les flottants sont stockés sous forme de valeurs binaires, tout comme les ints.

Une fois le calcul effectué, vous utilisez les pointeurs et le casting pour convertir le nombre entier en valeur flottante.

Ce n'est pas la même chose que de convertir la valeur en un flottant. Cela transformerait la valeur int 1 en valeur flottante 1.0. Dans ce cas, vous transformez la valeur int en un nombre à virgule flottante décrit par la valeur binaire stockée dans l'int.

C'est assez difficile à expliquer correctement. Je vais chercher un exemple :-)

Edit : Regardez ici : http://en.wikipedia.org/wiki/Fast_inverse_square_root

Votre code fait essentiellement la même chose que ce qui est décrit dans cet article.

2voto

StuartLC Points 35534

Re : Que fait-il ?

Il prend la valeur des octets stockés en int et interprète plutôt ces octets comme un float (sans conversion).

Heureusement, les flottants et les ints ont la même valeur. taille des données de 4 octets.

2voto

Mark Hurd Points 4746

Parce que le sergent Borsch l'a demandé, voici l'équivalent de l'Union :

[StructLayout(LayoutKind.Explicit)]
struct ByteFloatUnion {
  [FieldOffset(0)] internal byte byte0;
  [FieldOffset(1)] internal byte byte1;
  [FieldOffset(2)] internal byte byte2;
  [FieldOffset(3)] internal byte byte3;
  [FieldOffset(0)] internal float single;
}

public static float sample() {
   ByteFloatUnion result;
   result.single = 0f;
   result.byte0 = 154;
   result.byte1 = 153;
   result.byte2 = 25;
   result.byte3 = 64;

   return result.single;
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X