2009-12-10 7 views
1

J'écris un ensemble de fonctions de conversion de type numérique pour un moteur de base de données, et je suis préoccupé par le comportement de la conversion de grandes valeurs à virgule flottante types entiers avec une plus grande précision. Prenez par exemple la conversion d'un int de 32 bits en un flottant de précision simple de 32 bits. Le significande de 23 bits du flotteur donne environ 7 chiffres décimaux de précision, donc la conversion de tout int avec plus de 7 chiffres environ entraînera une perte de précision (ce qui est bien et attendu). Cependant, lorsque vous convertissez un flotteur de retour à un int, vous vous retrouvez avec des artefacts de sa représentation binaire dans les chiffres de poids faible:Arrondi à utiliser pour la conversion aller-retour int -> float -> int

#include <iostream> 
#include <iomanip> 

using namespace std; 

int main() 
{ 
    int a = 2147483000; 
    cout << a << endl; 
    float f = (float)a; 
    cout << setprecision(10) << f << endl; 
    int b = (int)f; 
    cout << b << endl; 

    return 0; 
} 

Cette impression:

2147483000 
2147483008 
2147483008 

008 talonnage, Au-delà de la précision du flottant, il semble donc indésirable de conserver dans l'int, puisque dans une application de base de données, les utilisateurs sont principalement concernés par la représentation décimale, et les 0 à la fin sont utilisés pour indiquer des chiffres insignifiants. Voici donc mes questions: y a-t-il des systèmes existants bien connus qui effectuent des arrondis décimaux significatifs dans les conversions float -> int (ou double -> long long), et y a-t-il des algorithmes connus et efficaces? alors? (Remarque: Je suis conscient que certains systèmes ont des types à virgule flottante décimale, tels que ceux définis par IEEE 754-2008, mais ils ne disposent pas du support matériel courant et ne sont pas intégrés en C/C++. Je veux les prendre en charge plus tard, mais j'ai toujours besoin de gérer les flottants binaires de manière intuitive.)

+0

Cette situation envoie un drapeau rouge, demande « Pourquoi est-il une conversion aller-retour? » Cela implique que la valeur doit être stockée dans la base de données sous la forme d'un entier et non d'un flottant. Un peu comme simplifier une expression algébrique. –

+0

Bonne question. Je ne prévois certainement pas qu'il y ait des conversions aller-retour, et rien dans le moteur de base de données lui-même ne provoquera cela. Cependant, cette base de données est conçue pour des données en mémoire très condensées, donc j'ai besoin d'utiliser les plus petits types possibles.Je ne veux pas que les utilisateurs (qui peuvent soumettre leurs propres requêtes de type SQL) pensent qu'ils obtiennent beaucoup plus de précision qu'ils ne le sont réellement quand ils convertissent un flottant binaire en un int ou un flottant décimal de plus haute précision. –

Répondre

1

std::numeric_limits<float>::digits10 indique que vous obtenez seulement 6 chiffres précis pour float.

Choisissez un algorithme efficace pour votre langue, processeur et distribution de données au calculate-the-decimal-length-of-an-integer (ou here). Puis soustrayez le nombre de chiffres que digits10 dit sont précis pour obtenir le nombre de chiffres à éliminer. Utilisez cela comme un index pour rechercher une puissance de 10 à utiliser comme un module. Etc:

Une préoccupation: Supposons que vous convertissiez un flottant en nombre décimal et effectuiez ce type d'arrondi ou de troncature. Ensuite, convertissez cette décimale "ajustée" en un flottant et revenez à une décimale avec le même schéma d'arrondi/troncature. Avez-vous la même valeur décimale? Esperons que oui.

Ce n'est pas vraiment ce que vous cherchez, mais peut-être une lecture intéressante: A Proposal to add a max significant decimal digits value to the C++ Standard Library Numeric limits

+0

Merci! J'avais négligé la présence de chiffres10 dans numeric_limits et j'avais introduit mon propre modèle de traits à cet effet (basé sur les bits du significand). Votre approche est essentiellement ce que j'avais décidé de faire, donc il est bon d'avoir une validation indépendante. Et cette proposition était en effet intéressante; il fournit une perspective supplémentaire sur ces questions. –

0

Naturellement, 2147483008 a des zéros de fin si vous l'écrivez en binaire (1111111111111111111110110000000) ou en hexadécimal (0b0x7FFFFD80). La chose la plus «correcte» à faire serait de suivre les chiffres insignifiants dans l'une de ces formes à la place. Alternativement, vous pouvez simplement mettre à zéro tous les chiffres après les sept premiers significatifs dans l'int (idéalement en arrondissant) après la conversion à partir d'un flottant, puisque le flotteur contient environ sept chiffres significatifs.

+0

Excellent point de clarification. Cependant, je ne m'attends pas vraiment à ce que binary ou hex soit utilisé pour afficher les données stockées dans cette base de données. Ma question se résume à la sagesse/précédent et aux moyens les plus efficaces de mettre en œuvre votre deuxième paragraphe. –

Questions connexes