web-dev-qa-db-fra.com

Obtenir la partie décimale d'un float sans utiliser modf ()

Je développe pour une plate-forme sans bibliothèque de mathématiques, je dois donc créer mes propres outils. Mon moyen actuel d'obtenir la fraction est de convertir le float en point fixe (multiplier par (float) 0xFFFF, transtyper en int), obtenir uniquement la partie inférieure (masque avec 0xFFFF) et le reconvertir en float.

Cependant, l'imprécision me tue. J'utilise mes fonctions Frac () et InvFrac () pour dessiner une ligne anti-aliasée. En utilisant modf, ma ligne est parfaitement lisse. Avec ma propre méthode, les pixels commencent à sauter en raison de la perte de précision.

Ceci est mon code:

const float fp_amount = (float)(0xFFFF);
const float fp_amount_inv = 1.f / fp_amount;

inline float Frac(float a_X)
{
    return ((int)(a_X * fp_amount) & 0xFFFF) * fp_amount_inv;
}

inline float Frac(float a_X)
{
    return (0xFFFF - (int)(a_X * fp_amount) & 0xFFFF) * fp_amount_inv;
}

Merci d'avance!

19
knight666

Si je comprends bien votre question, vous voulez juste la partie après le signe décimal? Vous n'en avez pas besoin réellement dans une fraction (numérateur et dénominateur entier)?

Donc nous avons un nombre, disons 3.14159 et nous voulons finir avec juste 0.14159. En supposant que notre numéro soit stocké dans float f;, nous pouvons le faire:

f = f-(long)f;

Qui, si nous insérons notre numéro, fonctionne comme ceci:

0.14159 = 3.14159 - 3;

Cela supprime la partie entière du nombre en laissant uniquement la partie décimale. Lorsque vous convertissez le float en long, la partie décimale est supprimée. Ensuite, lorsque vous soustrayez cela de votre float original, vous vous retrouvez avec seulement la partie décimale. Nous devons utiliser un long ici à cause de la taille du type float (8 octets sur la plupart des systèmes). Un entier (seulement 4 octets sur de nombreux systèmes) n'est pas nécessairement assez grand pour couvrir la même plage de nombres qu'un float, mais un long devrait l'être.

40
Daniel Bingham

Comme je le supposais, modf n'utilise pas d'arithmétique en soi - ce sont tous des décalages et des masques, jetez un oeil ici . Ne pouvez-vous pas utiliser les mêmes idées sur votre plate-forme?

7
AVB

Il y a un bug dans vos constantes. Vous essayez essentiellement de faire un décalage à gauche du nombre de 16 bits, masquer tout sauf les bits inférieurs, puis de nouveau à droite de 16 bits. Décaler revient à multiplier par une puissance de 2, mais vous n'utilisez pas une puissance de 2 - vous utilisez 0xFFFF, qui est désactivé par 1. Remplacer ceci par 0x10000 fera fonctionner la formule comme prévu.

4
Mark Ransom

Je recommanderais de regarder comment modf est implémenté sur les systèmes que vous utilisez aujourd'hui. Découvrez la version d'uClibc.

http://git.uclibc.org/uClibc/tree/libm/s_modf.c

(Pour des raisons juridiques, il semble être sous licence BSD, mais vous voudrez évidemment vérifier)

Certaines des macros sont définies ici .

4
Bill Lynch

Je ne suis pas tout à fait sûr, mais je pense que ce que vous faites est faux, puisque vous ne considérez que la mantisse et oubliez complètement l'exposant. 

Vous devez utiliser l'exposant pour décaler la valeur dans la mantisse afin de trouver la partie entière réelle.

Pour une description du mécanisme de stockage des flotteurs 32 bits, jetez un oeil ici

2
Bruno Brant

On dirait que tu veux peut-être ça.

float f = something;
float fractionalPart = f - floor(f);
1
Victor Engel

Pourquoi aller en virgule flottante pour votre dessin au trait? Vous pouvez simplement vous en tenir à votre version à point fixe et utiliser plutôt une routine de dessin de ligne basée sur un nombre entier/un point fixe - on pense à Bresenham. Bien que cette version ne soit pas aliasée, je sais qu’il en existe d’autres.

Dessin au trait de Bresenham

1
Michael Dorgan

Votre méthode suppose qu'il y a 16 bits dans la partie décimale (et comme le note Mark Ransom, cela signifie que vous devez passer de 16 bits, c'est-à-dire multiplier par 0x1000). Cela pourrait ne pas être vrai. L'exposant est ce qui détermine le nombre de bits dans la partie fractionnaire.

Pour mettre cela dans une formule, votre méthode fonctionne en calculant (x modf 1.0) en tant que ((x << 16) mod 1<<16) >> 16, et c'est ce 16 codé en dur qui devrait dépendre de l'exposant - le remplacement exact dépend de votre format flottant.

0
MSalters

Une option consiste à utiliser fmod(x, 1).

0
emlai