Y at-il un moyen d'obtenir les « chiffres significatifs » d'une décimale?
-
02-10-2019 - |
Question
Mise à jour
OK, après une enquête, et merci grande partie des réponses utiles fournies par Jon et Hans, ce que j'ai pu mettre ensemble. Jusqu'à présent, je pense qu'il semble bien fonctionner. Je ne parierais pas ma vie sur l'exactitude totale, bien sûr.
public static int GetSignificantDigitCount(this decimal value)
{
/* So, the decimal type is basically represented as a fraction of two
* integers: a numerator that can be anything, and a denominator that is
* some power of 10.
*
* For example, the following numbers are represented by
* the corresponding fractions:
*
* VALUE NUMERATOR DENOMINATOR
* 1 1 1
* 1.0 10 10
* 1.012 1012 1000
* 0.04 4 100
* 12.01 1201 100
*
* So basically, if the magnitude is greater than or equal to one,
* the number of digits is the number of digits in the numerator.
* If it's less than one, the number of digits is the number of digits
* in the denominator.
*/
int[] bits = decimal.GetBits(value);
if (value >= 1M || value <= -1M)
{
int highPart = bits[2];
int middlePart = bits[1];
int lowPart = bits[0];
decimal num = new decimal(lowPart, middlePart, highPart, false, 0);
int exponent = (int)Math.Ceiling(Math.Log10((double)num));
return exponent;
}
else
{
int scalePart = bits[3];
// Accoring to MSDN, the exponent is represented by
// bits 16-23 (the 2nd word):
// http://msdn.microsoft.com/en-us/library/system.decimal.getbits.aspx
int exponent = (scalePart & 0x00FF0000) >> 16;
return exponent + 1;
}
}
Je l'ai pas testé tout ce fond. Voici quelques entrées / sorties d'échantillons, bien que:
Value Precision 0 1 digit(s). 0.000 4 digit(s). 1.23 3 digit(s). 12.324 5 digit(s). 1.2300 5 digit(s). -5 1 digit(s). -5.01 3 digit(s). -0.012 4 digit(s). -0.100 4 digit(s). 0.0 2 digit(s). 10443.31 7 digit(s). -130.340 6 digit(s). -80.8000 6 digit(s).
En utilisant ce code, j'imagine que j'accomplir mon but en faisant quelque chose comme ceci:
public static decimal DivideUsingLesserPrecision(decimal x, decimal y)
{
int xDigitCount = x.GetSignificantDigitCount();
int yDigitCount = y.GetSignificantDigitCount();
int lesserPrecision = System.Math.Min(xDigitCount, yDigitCount);
return System.Math.Round(x / y, lesserPrecision);
}
Je n'ai pas vraiment fini de travailler à travers cela, cependant. Toute personne qui veut partager des idées: ce serait très apprécié
Question originale
Supposons que j'écrire ce code:
decimal a = 1.23M;
decimal b = 1.23000M;
Console.WriteLine(a);
Console.WriteLine(b);
Le ci-dessus en sortie:
1.23 1.23000
Je trouve que cela fonctionne aussi si j'utilise decimal.Parse("1.23")
pour a
et decimal.Parse("1.23000")
pour b
(ce qui signifie que cette question s'applique aux cas où reçoit l'entrée d'utilisateur du programme).
Il apparaît donc clairement une valeur decimal
est en quelque sorte « au courant » de ce que je vais appeler son précision . Cependant, je ne vois aucun membre sur le type de decimal
qui fournissent une façon d'accéder à ce, en dehors de ToString
lui-même.
Supposons que je voulais multiplier deux valeurs decimal
et taillez le résultat à la précision de l'argument moins précis. En d'autres termes:
decimal a = 123.4M;
decimal b = 5.6789M;
decimal x = a / b;
Console.WriteLine(x);
Les sorties ci-dessus:
21.729560302171195125816619416
Ce que je vous pose la question est: comment pourrais-je écrire une méthode qui retournerait 21.73
à la place (depuis 123.4M
a quatre chiffres significatifs)
Pour être clair: je me rends compte que je pourrais appeler ToString
sur les deux arguments, compter les chiffres significatifs dans chaque chaîne, et utiliser ce numéro pour arrondir le résultat du calcul. Je cherche un différent façon, si possible.
(I aussi se rendre compte que dans la plupart des scénarios où vous avez affaire à des chiffres significatifs, vous n'avez pas besoin probablement d'être en utilisant le type de decimal
. Mais je demande parce que, comme je l'ai mentionné au début, le type de decimal
apparaît pour inclure des informations sur la précision, alors que double
n'a pas, pour autant que je sais.)
La solution
Vous pouvez utiliser Decimal.GetBits
pour obtenir le données brutes, et le travail hors de cela.
Malheureusement, je n'ai pas le temps de l'exemple de code d'écriture au moment - et vous voudrez probablement utiliser
Autres conseils
Oui, à la différence flottante types de points, System.Decimal garde trace du nombre de chiffres dans le littéral. Ceci est une caractéristique de decimal.Parse (), que ce soit exécuté par votre code vous-même ou par le compilateur lorsqu'il analyse un littéral dans votre programme. Vous pouvez récupérer ces informations, consultez le code dans ma réponse dans ce fil .
Récupérer le nombre de chiffres significatifs après que vous faites des mathématiques sur la valeur me paraît un long shot. Aucune idée si les opérateurs les conserver, s'il vous plaît laissez-nous savoir ce que vous découvrez.
La solution ci-dessus tombe du lit rapidement avec des valeurs telles que 1200 où le nombre de chiffres significatifs est 2, mais la fonction retourne 4. Peut-être il y a une manière cohérente pour faire face à une telle situation ex.chèque pour vous assurer que la valeur de rendement ne comprennent des zéros de fin dans la partie entière du nombre sans partie décimale.