2 votes

Comment écrire une fonction std::floor à partir de zéro ?

J'aimerais savoir comment écrire ma propre fonction plancher pour arrondir un flottant à la baisse.

Est-il possible de le faire en mettant à 0 les bits d'un flottant qui représentent les chiffres après la virgule ?

Si oui, comment puis-je accéder à ces bits et les modifier ?

Merci.

4voto

Chris Dodd Points 39013

Vous pouvez faire des manipulations de bits sur des nombres à virgule flottante, mais pour bien faire les choses, il faut savoir exactement quelle est la représentation binaire de la virgule flottante. Pour la plupart des machines de nos jours, c'est IEEE-754 ce qui est assez simple. Par exemple, les flottants 32 bits IEEE-754 ont 1 bit de signe, 8 bits d'exposant et 23 bits de mantisse. Vous pouvez donc utiliser des décalages et des masques pour extraire ces champs et en faire quelque chose. Il est donc assez facile de faire des troncatures (arrondir à l'entier vers 0) :

float trunc(float x) {
    union {
        float    f;
        uint32_t i;
    } val;
    val.f = x;
    int exponent = (val.i >> 23) & 0xff; // extract the exponent field;
    int fractional_bits = 127 + 23 - exponent;
    if (fractional_bits > 23) // abs(x) < 1.0
        return 0.0;
    if (fractional_bits > 0)
        val.i &= ~((1U << fractional_bits) - 1);
    return val.f;
}

Tout d'abord, nous extrayons le champ de l'exposant et l'utilisons pour calculer le nombre de bits après l'élément le point décimal sont présents dans le nombre. S'il y en a plus que la taille de la mantisse, nous retournons simplement 0. Sinon, s'il y en a au moins 1, nous masquons (effaçons) le nombre de bits de poids faible. C'est assez simple. Nous ignorons les dénormaux, NaN, et l'infini, mais cela ne pose pas de problème, car ils ont des exposants de tous les 0 ou tous les 1, ce qui signifie que nous finissons par convertir les dénormaux en 0 (ils sont pris dans le premier si, avec les petits nombres normaux), et laisser NaN/Inf inchangé.

Pour faire un plancher, il faut aussi regarder le signe et arrondir les nombres négatifs vers le haut, vers l'infini négatif.

Notez que cela est certainement plus lent que d'utiliser des instructions dédiées à la virgule flottante, donc ce genre de chose n'est vraiment utile que si vous avez besoin d'utiliser des nombres en virgule flottante sur du matériel qui n'a pas de support natif de la virgule flottante. Ou si vous voulez simplement jouer et apprendre comment ces choses fonctionnent à un bas niveau.

3voto

Dat Chu Points 3810

Définir à partir de zéro. Et non, mettre à 0 les bits de votre nombre à virgule flottante représentant les chiffres après la virgule ne fonctionnera pas. Si vous regardez IEEE-754 vous verrez que vous avez en fait tous vos nombres à virgule flottante dans la forme :

0.xyzxyzxyz 2^(abc)

Donc, pour mettre en œuvre le plancher, vous pouvez obtenir le xyzxyzxyz et décaler à gauche par abc+1 fois. Laissez tomber le reste. Je vous suggère de vous documenter sur la représentation binaire d'un nombre à virgule flottante (lien ci-dessus), cela devrait éclairer la solution que j'ai proposée.

REMARQUE : Vous devez également vous occuper du bit de signe. Et la mantisse de votre nombre est décalée de 127.

Voici un exemple, disons que vous avez le nombre pi : 3,14..., vous voulez obtenir 3.

Pi est représenté en binaire par

0 10000000 10010010000111111011011

Cela se traduit par

sign = 0 ; e = 1 ; s = 110010010000111111011011

Ce qui précède provient directement de Wikipedia . Puisque e est 1, vous voudrez décaler s gauche de 1 + 1 = 2, de sorte que vous obtenez 11 => 3.

0voto

Mooing Duck Points 27497
#include <iostream>
#include <iomanip>

double round(double input, double roundto) {
    return int(input / roundto) * roundto;
}

int main() {
    double pi = 3.1415926353898;
    double almostpi = round(pi, 0.0001);
    std::cout << std::setprecision(14) << pi << '\n' << std::setprecision(14) << almostpi;
}

http://ideone.com/mdqFA sortie :

3.1415926353898
3.1415

Cela sera bien plus rapide que n'importe quel bidouillage de bits que vous pouvez imaginer. Et cela fonctionne sur tous les ordinateurs (avec des flottants) au lieu d'un seul type.

0voto

Le transfert vers unsigned tout en retournant un double fait ce que vous cherchez, mais sous le capot. Ce simple morceau de code fonctionne pour tout nombre POSITIF.

#include <iostream>

double floor(const double& num) {
    return (unsigned long long) num; 
}

0voto

Lakshya Raj Points 816

Cela a été testé sur tio.run (Essayez-le en ligne) y onlinegdb.com . La fonction elle-même ne nécessite pas de #include mais pour imprimer les réponses, j'ai inclus stdio.h (dans le tio.run y onlinegdb.com pas ici). C'est ici :

long double myFloor(long double x) /* Change this to your liking: long double might
                                             be float in your situation.  */
{
    long double xcopy=x<0?x*-1:x;
    unsigned int zeros=0;
    long double n=1;
    for(n=1;xcopy>n*10;n*=10,++zeros);
    for(xcopy-=n;zeros!=-1;xcopy-=n)
        if(xcopy<0)
        {
            xcopy+=n;
            n/=10;
            --zeros;
        }
    xcopy+=n;
    return x<0?(xcopy==0?x:x-(1-xcopy)):(x-xcopy);
}

Cette fonction fonctionne partout (à peu près sûr) car elle supprime simplement toutes les parties non décimales au lieu d'essayer de travailler avec les parties des flottants.

Le plancher d'un nombre à virgule flottante est le plus grand nombre entier inférieur ou égal à celui-ci. Voici quelques exemples :

floor(5.7)  = 5
floor(3)    = 3
floor(9.9)  = 9
floor(7.0)  = 7
floor(-7.9) = -8
floor(-5.0) = -5
floor(-3.3) = -3
floor(0)    = 0
floor(-0.0) = -0
floor(-0)   = -0

Remarque : il s'agit d'une copie quasi exacte de mon autre réponse qui répondait à une question qui était essentiellement la même que celle-ci.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X