Float32 à Float16

Quelqu’un peut-il m’expliquer comment je convertis une valeur à virgule flottante 32 bits en une valeur à virgule flottante 16 bits?

(s = signe e = exposant et m = mantisse)

Si float 32 bits est 1s7e24m
Et le float 16 bits est 1s5e10m

Alors est-ce aussi simple que de faire?

int fltInt32; short fltInt16; memcpy( &fltInt32, &flt, sizeof( float ) ); fltInt16 = (fltInt32 & 0x00FFFFFF) >> 14; fltInt16 |= ((fltInt32 & 0x7f000000) >> 26) <> 16); 

Je suppose que ce n’est pas si simple … alors quelqu’un peut-il me dire ce que vous devez faire?

Edit: Je vois que mon décalage d’exposant est erroné … C’est donc mieux?

 fltInt16 = (fltInt32 & 0x007FFFFF) >> 13; fltInt16 |= (fltInt32 & 0x7c000000) >> 13; fltInt16 |= (fltInt32 & 0x80000000) >> 16; 

J’espère que c’est correct. Excuses si je manque quelque chose d’évident qui a été dit. Il est presque minuit un vendredi soir … donc je ne suis pas “entièrement” sobre;)

Edit 2: Ooops. Enculé à nouveau. Je veux perdre le top 3 bits pas le plus bas! Alors que diriez-vous de ceci:

 fltInt16 = (fltInt32 & 0x007FFFFF) >> 13; fltInt16 |= (fltInt32 & 0x0f800000) >> 13; fltInt16 |= (fltInt32 & 0x80000000) >> 16; 

Le code final devrait être :

 fltInt16 = ((fltInt32 & 0x7fffffff) >> 13) - (0x38000000 >> 13); fltInt16 |= ((fltInt32 & 0x80000000) >> 16); 

Les exposants de vos représentations float32 et float16 sont probablement biaisés et biaisés différemment. Vous devez annuler l’exposant que vous avez obtenu de la représentation float32 pour obtenir l’exposant réel, puis le biaiser pour la représentation float16.

Mis à part ce détail, je pense que c’est aussi simple que cela, mais je suis toujours surpris par les représentations en virgule flottante de temps en temps.

MODIFIER:

  1. Vérifiez le débordement lorsque vous faites la chose avec les exposants pendant que vous y êtes.

  2. Votre algorithme tronque les derniers bits de la mantisa un peu brusquement, ce qui peut être acceptable, mais vous voudrez peut-être implémenter, par exemple, le tour au plus proche en regardant les bits qui sont sur le point d’être ignorés. “0 …” -> arrondi, “100..001 …” -> arrondi, “100..00” -> arrondi à égal.

L’exposant doit être impartial, pincé et repositionné. C’est le code rapide que j’utilise:

 unsigned int fltInt32; unsigned short fltInt16; fltInt16 = (fltInt32 >> 31) << 5; unsigned short tmp = (fltInt32 >> 23) & 0xff; tmp = (tmp - 0x70) & ((unsigned int)((int)(0x70 - tmp) >> 4) >> 27); fltInt16 = (fltInt16 | tmp) << 10; fltInt16 |= (fltInt32 >> 13) & 0x3ff; 

Ce code sera encore plus rapide avec une table de correspondance pour l’exposant, mais j’utilise celui-ci car il est facilement adapté à un stream de travail SIMD.

Limites de la mise en oeuvre:

  • Les valeurs qui débordent et qui ne peuvent pas être représentées dans float16 donneront des valeurs non définies.
  • Les valeurs insuffisantes renverront une valeur indéfinie comprise entre 2^-15 et 2^-14 au lieu de zéro.
  • Les denormaux donneront des valeurs indéfinies.

Soyez prudent avec les dénormaux. Si votre architecture les utilise, ils risquent de ralentir considérablement votre programme.

Voici le lien vers un article sur IEEE754, qui donne la disposition et les biais des bits.

http://en.wikipedia.org/wiki/IEEE_754-2008