Comment effectuer une opération bitwise sur des nombres à virgule flottante

j'ai essayé ceci:

float a = 1.4123;
a = a & (1 << 3);

j'obtiens une erreur de compilateur disant que l'opérande de & ne peut pas être de type float.

Quand je fais:

float a = 1.4123;
a = (int)a & (1 << 3);

je lance le programme. La seule chose est que l'opération bitwise est faite sur la représentation entière du nombre obtenu après arrondissement.

n'est pas autorisé.

float a = 1.4123;
a = (void*)a & (1 << 3);

I Je ne comprends pas pourquoi int peut être moulé en void* mais pas float .

je fais ceci pour résoudre le problème décrit dans la question de débordement de pile comment résoudre des équations linéaires à l'aide d'un algorithme génétique? .

39
demandé sur Community 2009-11-12 19:36:15

9 réponses

au niveau de la langue, il n'y a pas de "bitwise operation on floating-point numbers". Les opérations Bitwise en C / C++ travaillent sur la valeur-représentation d'un nombre. Et la valeur-représentation des nombres à virgule flottante n'est pas définie dans C/C++. Les nombres à virgule flottante n'ont pas de bits au niveau de la représentation de la valeur, c'est pourquoi vous ne pouvez pas leur appliquer des opérations bitwise.

Tout ce que vous pouvez faire est d'analyser le contenu bit de la mémoire brute occupée par le nombre à virgule flottante. Pour cela, vous devez soit utiliser une union comme suggéré ci-dessous ou (de manière équivalente, et seulement en C++) réinterpréter l'objet à virgule flottante comme un tableau d'objets unsigned char , comme dans

float f = 5;
unsigned char *c = reinterpret_cast<unsigned char *>(&f);
// inspect memory from c[0] to c[sizeof f - 1]

et s'il vous plaît, n'essayez pas de réinterpréter un objet float comme un objet int , comme d'autres réponses le suggèrent. Cela n'a pas beaucoup de sens, c'est illégal, et ce n'est pas garanti de travailler dans des compilateurs qui suivent des règles d'aliasing strictes dans optimisation. Le seul moyen légal d'inspecter le contenu de la mémoire en C++ est de le réinterpréter comme un tableau de [signed/unsigned] char .

notez également que vous n'êtes pas techniquement garanti que la représentation en virgule flottante sur votre système est IEEE754 (bien que dans la pratique, il est à moins que vous ne l'autorisez explicitement à ne pas être, et alors seulement en ce qui concerne -0.0, ±infini et NaN).

66
répondu AnT 2014-11-13 23:28:50

si vous essayez de changer les bits dans la représentation en virgule flottante, vous pouvez faire quelque chose comme ceci:

union fp_bit_twiddler {
    float f;
    int i;
} q;
q.f = a;
q.i &= (1 << 3);
a = q.f;

comme AndreyT le note, accéder à une union comme celle-ci invoque un comportement indéfini, et le compilateur pourrait faire pousser des bras et vous étrangler. Faire ce qu'il suggère à la place.

19
répondu mob 2012-02-22 20:47:46
float a = 1.4123;
unsigned int* inta = reinterpret_cast<unsigned int*>(&a);
*inta = *inta & (1 << 3);
9
répondu Chap 2009-11-12 18:41:32

Regardez ce qui suit. Inspiré par la racine carrée inverse rapide:

#include <iostream>
using namespace std;

int main()
{
    float x, td = 2.0;
    int ti = *(int*) &td;
    cout << "Cast int: " << ti << endl;
    ti = ti>>4;
    x = *(float*) &ti;
    cout << "Recast float: " << x << endl;
    return 0; 
}
6
répondu Justin 2009-11-13 11:02:21

@mobrule:

mieux:

#include <stdint.h>
...
union fp_bit_twiddler {
    float f;
    uint32_t u;
} q;

/* mutatis mutandis ... */

Pour ces valeurs int sera probablement ok, mais en général, vous devez utiliser ints non signés pour le bit shifting pour éviter les effets des changements arithmétiques. Et l'uint32_t fonctionnera même sur les systèmes dont les ints ne sont pas 32 bits.

4
répondu Tim Schaeffer 2009-11-12 16:56:13

l'implémentation de Python dans les opérations en bitwise à virgule flottante (recette Python)" des opérations en bitwise à virgule flottante fonctionne en représentant des nombres en binaire qui s'étend à l'infini vers la gauche aussi bien que vers la droite à partir du point fractionnel. Parce que les nombres à virgule flottante ont un zéro signé sur la plupart des architectures, il utilise pour représenter les nombres négatifs (en fait, il fait semblant pour ce faire et utilise quelques astuces pour atteindre l'apparence).

je suis sûr qu'il peut être adapté pour fonctionner en C++, mais il faut prendre soin de ne pas laisser les bons changements déborder lors de l'égalisation des exposants.

2
répondu Pyry Pakkanen 2012-02-06 19:26:43
Les opérateurs Bitwise

ne doivent pas être utilisés sur les flotteurs, car les flotteurs sont spécifiques au matériel, quelle que soit la similitude de matériel que vous ayez. Quel projet / Travail voulez-vous risquer sur "bien ça a fonctionné sur ma machine"? Au lieu de cela, pour C++, vous pouvez obtenir un "feel" similaire pour les opérateurs de bit shift en surchargeant l'opérateur stream sur un" objet "wrapper pour un float:

// Simple object wrapper for float type as templates want classes.
class Float
{
float m_f;
public:
    Float( const float & f )
    : m_f( f )
    {
    }

    operator float() const
    {
        return m_f;
    }
};

float operator>>( const Float & left, int right )
{
    float temp = left;
    for( right; right > 0; --right )
    {
        temp /= 2.0f;
    }
    return temp;
}

float operator<<( const Float & left, int right )
{
    float temp = left;
    for( right; right > 0; --right )
    {
        temp *= 2.0f;
    }
    return temp;
}

int main( int argc, char ** argv )
{
    int a1 = 40 >> 2; 
    int a2 = 40 << 2;
    int a3 = 13 >> 2;
    int a4 = 256 >> 2;
    int a5 = 255 >> 2;

    float f1 = Float( 40.0f ) >> 2; 
    float f2 = Float( 40.0f ) << 2;
    float f3 = Float( 13.0f ) >> 2;
    float f4 = Float( 256.0f ) >> 2;
    float f5 = Float( 255.0f ) >> 2;
}

vous aurez un reste, que vous pouvez jeter en fonction de votre désir application.

1
répondu Kit10 2012-02-08 11:53:14
float a = 1.4123;
int *b = (int *)&a;
*b = *b & (1 << 3);
// a is now the IEEE floating-point value caused by the manipulation of *b
// equals 1.121039e-44 (tested on my system)

c'est similaire à la réponse de Justin, sauf qu'il ne crée une vue des bits dans les mêmes registres que a . Ainsi, lorsque vous manipulez *b , la valeur de a change en conséquence.

1
répondu Patrick Roberts 2015-03-05 18:26:23

FWIW, il y a un vrai cas d'utilisation pour les opérations bit-wise sur floating point (je viens de le rencontrer récemment) - shaders écrits pour les GPUs qui ne prennent en charge que les anciennes versions de GLSL (1.2 et antérieures n'avaient pas de support pour les opérateurs bit-wise), et où il y aurait perte de précision si les flotteurs étaient convertis en ints.

les opérations bit-wise peuvent être mises en œuvre sur des nombres à virgule flottante en utilisant des restes (modulo) et des vérifications d'inégalité. Exemple:

float A = 0.625; //value to check; ie, 160/256
float mask = 0.25; //bit to check; ie, 1/4
bool result = (mod(A, 2.0 * mask) >= mask); //non-zero if bit 0.25 is on in A

ce qui précède suppose que A se situe entre [0..1) et qu'il n'y a qu'un "bit" dans le masque à vérifier, mais il pourrait être généralisé pour les cas plus complexes.

Cette idée est basée sur certaines infos trouvées sur est-il-possible-pour-mettre en œuvre-bit-à-bit-opérateurs-aide-entier-de l'arithmétique

S'il n'y a même pas de fonction mod intégrée, cela peut aussi être implémenté assez facilement. Exemple:

float mod(float num, float den)
{
    return num - den * floor(num / den);
}
0
répondu djulien 2017-05-23 12:34:10