C'è un modo per ottenere le “figure significative” di un numero decimale?
-
02-10-2019 - |
Domanda
Aggiorna
OK, dopo alcune indagini, e grazie in gran parte alle risposte utili forniti da Jon e Hans, questo è quello che sono stato in grado di mettere insieme. Finora penso che sembra funzionare bene. Non ci scommetterei la mia vita sulla sua totale correttezza, naturalmente.
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;
}
}
Non ho provato tutto ciò che bene. Ecco alcuni ingressi / uscite di esempio, se:
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).
Con questo codice, immagino vorrei realizzare il mio obiettivo facendo qualcosa di simile a questo:
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);
}
Non ho veramente finito di lavorare attraverso questo, però. Chiunque voglia pensieri di condivisione: che sarebbe molto apprezzato
Original domanda
Supponiamo che io ho scrivere questo codice:
decimal a = 1.23M;
decimal b = 1.23000M;
Console.WriteLine(a);
Console.WriteLine(b);
L'output volontà di cui sopra:
1.23 1.23000
Trovo che questo funziona anche se uso decimal.Parse("1.23")
per a
e decimal.Parse("1.23000")
per b
(il che significa che a questa domanda si applica ai casi in cui il programma riceve l'input dell'utente).
Quindi, chiaramente un valore decimal
è in qualche modo "consapevole" di ciò che io chiamo il suo di precisione . Tuttavia, non vedo membri sul tipo decimal
che forniscono alcun modo di accedere a questo, a parte ToString
sé.
Supponiamo che io volevo per moltiplicare due valori decimal
e tagliare il risultato alla precisione dell'argomento meno preciso. In altre parole:
decimal a = 123.4M;
decimal b = 5.6789M;
decimal x = a / b;
Console.WriteLine(x);
Le uscite precedenti:
21.729560302171195125816619416
Quello che mi chiedo è: come potrei scrivere un metodo che sarebbe tornato 21.73
invece (dal 123.4M
ha quattro cifre significative)
Per essere chiari: realizzo ho potuto chiamare ToString
su entrambi gli argomenti, contare le cifre significative in ogni stringa, e utilizzare questo numero per arrotondare il risultato del calcolo. Sto cercando un diverso modo, se possibile.
(I anche rendersi conto che nella maggior parte degli scenari in cui hai a che fare con le figure significative, probabilmente non è necessario essere utilizzando il tipo di decimal
. Ma mi sto chiedendo perché, come ho già detto in principio, viene visualizzato il tipo decimal
per includere informazioni sulla precisione, mentre double
non lo fa, per quanto ne so.)
Soluzione
È possibile utilizzare Decimal.GetBits
per ottenere il dati grezzi, e il lavoro fuori da questo.
Purtroppo non ho il tempo di scrivere il codice di esempio in questo momento - e probabilmente desidera utilizzare BigInteger
per alcuni di manipolazione, se si sta utilizzando .NET 4 - ma spero che questo ti porterà in corso. Basta lavorare fuori la precisione e quindi chiamando Math.Round
sul risultato originale potrebbe essere un buon inizio.
Altri suggerimenti
Sì, a differenza di galleggiamento tipi di punti, System.Decimal tiene traccia del numero di cifre nel letterale. Questa è una caratteristica di decimal.Parse (), se eseguito dal codice voi stessi o dal compilatore quando si analizza un letterale nel vostro programma. È possibile recuperare queste informazioni, controllare il codice nella mia risposta in questa discussione .
mi Recuperare il numero di cifre significative dopo si fa la matematica sul valore colpisce come un colpo lungo. Non ho idea se gli operatori li conservano, per favore fateci sapere cosa si scopre.
La soluzione sopra cade dal letto rapidamente con valori come 1200 dove il numero di cifre significative è 2 ma la funzione restituisce 4. Forse c'è un modo coerente per affrontare una tale situazione IE controllare per non certo valore di ritorno non comprendono zeri nella porzione di numero intero senza una parte frazionaria.