Question

En C ++, quel est le moyen générique de convertir une valeur en virgule flottante en point fixe (int, 16:16 ou 24: 8)?

MODIFIER: Par souci de clarté, les valeurs en virgule fixe comportent deux parties: une partie entière et une partie fractionnaire. La partie entière peut être représentée par un type de données entier signé ou non signé. La partie fractionnaire est représentée par un type de données entier non signé.

Faisons une analogie avec l'argent pour plus de clarté. La fraction peut représenter des cents - une fraction d'un dollar. La plage du type de données "cents" serait comprise entre 0 et 99. Si un entier non signé de 8 bits devait être utilisé pour les calculs en virgule fixe, la partie fractionnaire serait alors divisée en 256 parties également divisibles.

J'espère que cela clarifie les choses.

Était-ce utile?

La solution

Voilà:

// A signed fixed-point 16:16 class
class FixedPoint_16_16
{
    short          intPart;
    unsigned short fracPart;

public:
    FixedPoint_16_16(double d)
    {
        *this = d; // calls operator=
    }

    FixedPoint_16_16& operator=(double d)
    {
        intPart = static_cast<short>(d);
        fracPart = static_cast<unsigned short>
                    (numeric_limits<unsigned short> + 1.0)*d);
        return *this;
    }

    // Other operators can be defined here
};

EDIT: Voici un cours plus général basé sur une autre façon courante de traiter les nombres à virgule fixe (et que KPexEA a souligné):

template <class BaseType, size_t FracDigits>
class fixed_point
{
    const static BaseType factor = 1 << FracDigits;

    BaseType data;

public:
    fixed_point(double d)
    {
        *this = d; // calls operator=
    }

    fixed_point& operator=(double d)
    {
        data = static_cast<BaseType>(d*factor);
        return *this;
    }

    BaseType raw_data() const
    {
        return data;
    }

    // Other operators can be defined here
};


fixed_point<int, 8> fp1;           // Will be signed 24:8 (if int is 32-bits)
fixed_point<unsigned int, 16> fp1; // Will be unsigned 16:16 (if int is 32-bits)

Autres conseils

Un transtypage de float à entier jettera la fraction. Si vous souhaitez conserver cette fraction en tant que point fixe, il vous suffit de multiplier le float avant de le lancer. Le code ci-dessous ne vérifie pas le débordement, n’oubliez pas.

Si vous voulez 16:16

double f = 1.2345;
int n;

n=(int)(f*65536);

si vous voulez 24: 8

double f = 1.2345;
int n;

n=(int)(f*256);

**** Edit **: Mon premier commentaire s’applique avant le montage de Kevin, mais je le laisse ici pour la postérité. Les réponses changent si vite ici parfois!

Le problème de l’approche de Kevin est qu’avec Fixed Point, vous composez normalement une taille de mot garantie (généralement 32 bits). Déclarer les deux parties séparément vous laisse au caprice de la structure de votre compilateur. Oui, vous pouvez le forcer, mais cela ne fonctionne pas pour une représentation autre que 16:16.

KPexEA est plus proche de la marque en emballant le tout dans int - bien que j'utiliserais "signé long". pour essayer d'être explicite sur 32bits. Ensuite, vous pouvez utiliser son approche pour générer la valeur de point fixe, et le découpage en bits extrait à nouveau les composants. Sa suggestion couvre également l’affaire 24: 8.

(Et tous les autres qui ont suggéré simplement static_cast ..... à quoi pensiez-vous?;))

J'ai donné la réponse au type qui a écrit la meilleure réponse, mais j'ai vraiment utilisé un code de question connexe qui pointe ici .

Il utilisait des modèles et était facile à supprimer des dépendances sur la librairie boost.

Cela convient très bien pour convertir un nombre à virgule flottante en entier, mais le PO voulait également un point fixe .

Maintenant, comment vous feriez cela en C ++, je ne le sais pas (le C ++ n'est pas quelque chose dans lequel je peux facilement penser). Essayez peut-être une approche de nombre entier mis à l’échelle, c’est-à-dire utilisez un entier de 32 ou 64 bits et allouez par programme les 6 derniers chiffres, par exemple, à ce qui se trouve à droite du point décimal.

Il n'y a pas de support intégré en C ++ pour les nombres à virgule fixe. Votre meilleur choix serait d'écrire une classe wrapper 'FixedInt' qui prend des doubles et les convertit.

En ce qui concerne une méthode générique pour convertir ... la partie int est assez simple, il suffit de saisir la partie entière de la valeur et de la stocker dans les bits supérieurs ... la partie décimale correspond à quelque chose comme:

for (int i = 1; i <= precision; i++)
{
   if (decimal_part > 1.f/(float)(i + 1)
   {
      decimal_part -= 1.f/(float)(i + 1);
      fixint_value |= (1 << precision - i);
   }
}

bien que cela contienne probablement encore des bugs

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top