Conversion de la point flottante en point fixe
en C++, quelle est la façon générique de convertir n'importe quelle valeur flottante (float) en point fixe (int, 16: 16 ou 24: 8)?
EDIT: pour clarifier, les valeurs à point fixe ont 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 par souci de clarté. La partie fractionnaire peut représenter cents -- une partie fractionnaire d'un dollar. La fourchette du type de données "cents" serait de 0 à 99. Si un entier non signé de 8 bits devait être utilisé pour les mathématiques à point fixe, alors la partie fractionnaire serait divisée en 256 parties également divisibles.
j'espère que efface les choses.
6 réponses
Ici, vous allez:
// 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 une classe plus générale basée sur une autre manière commune de traiter les nombres à points fixes (et que KPexEA a fait remarquer):
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)
un moulage de flotteur à entier va jeter la partie fractionnaire donc si vous voulez garder cette fraction autour comme point fixe, alors vous venez de multiplier le flotteur avant de le couler. Le code ci-dessous ne vérifiera pas de débordement d'Esprit vous.
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 l'édition de Kevin, mais je vais le laisser ici pour la postérité. Réponses changer si vite parfois!
le problème avec L'approche de Kevin est qu'avec un point fixe vous êtes normalement empaqueter dans une taille de mot garantie (typiquement 32bits). Déclarer les deux parties séparément vous laisse au caprice de l'empaquetage de la structure de votre compilateur. Oui, vous pouvez le forcer, mais il ne fonctionne pas pour rien d'autre que 16:16 représentation.
KPexEA est plus proche de la marque en emballant 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 BIT slicing extraire les composants à nouveau. Sa suggestion couvre également l'affaire 24:8.
(et tous ceux qui ont suggéré juste static_cast.....que pensiez-vous? ;))
c'est très bien pour passer de la virgule flottante à l'entier, mais L'O. P. voulait aussi point fixe.
maintenant comment vous feriez cela en C++, Je ne sais pas (C++ n'étant pas quelque chose que je peux penser facilement). Peut-être essayer une approche d'échelle-entier, c.-à-d. utiliser un 32 ou 64 bits entier et programmatically attribuer le dernier, disons, 6 chiffres à ce qui est sur le côté droit de la virgule décimale.
Il n'y a pas de support intégré en C++ pour les points fixes. Votre meilleur pari serait d'écrire une classe d'emballage "FixedInt" qui prend des doubles et les convertit.
Comme pour une méthode générique pour convertir... la partie int est assez facile, il suffit de saisir la partie entière de la valeur et de la stocker dans les bits supérieurs... partie décimale serait quelque chose le long des lignes de:
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 soit susceptible de contenir encore des bogues