Je veux raccourcir un nombre au premier chiffre significatif qui n'est pas 0. Les chiffres derrière doivent être arrondis.
Exemples:
0.001 -> 0.001
0.00367 -> 0.004
0.00337 -> 0.003
0.000000564 -> 0.0000006
0.00000432907543029 -> 0.000004
Actuellement, j'ai la procédure suivante:
if (value < (decimal) 0.01)
{
value = Math.Round(value, 4);
}
Remarque:
Comme vous pouvez le constater dans les exemples ci-dessus, un arrondi à 4 décimales peut ne pas être suffisant et la valeur peut varier considérablement.
Je déclarerais la variable precision
et utiliserais une itération, multipliant cette variable par 10
avec la valeur d'origine non frappée, cette precision
ajoutera 1
.
utilisez ensuite la variable precision
comme étant le second paramètre Math.Round
.
static decimal RoundFirstSignificantDigit(decimal input) {
int precision = 0;
var val = input;
while (Math.Abs(val) < 1)
{
val *= 10;
precision++;
}
return Math.Round(input, precision);
}
Je voudrais écrire une méthode d'extension pour cette fonction.
public static class FloatExtension
{
public static decimal RoundFirstSignificantDigit(this decimal input)
{
int precision = 0;
var val = input;
while (Math.Abs(val) < 1)
{
val *= 10;
precision++;
}
return Math.Round(input, precision);
}
}
puis utiliser comme
decimal input = 0.00001;
input.RoundFirstSignificantDigit();
Résultat
(-0.001m).RoundFirstSignificantDigit() -0.001
(-0.00367m).RoundFirstSignificantDigit() -0.004
(0.000000564m).RoundFirstSignificantDigit() 0.0000006
(0.00000432907543029m).RoundFirstSignificantDigit() 0.000004
Quelque chose comme ca ?
public decimal SpecialRound(decimal value)
{
int posDot = value.ToString().IndexOf('.'); // Maybe use something about cultural (in Fr it's ",")
if(posDot == -1)
return value;
int posFirstNumber = value.ToString().IndexOfAny(new char[9] {'1', '2', '3', '4', '5', '6', '7', '8', '9'}, posDot);
return Math.Round(value, posFirstNumber);
}
le code est de R
mais l'algo devrait être évident
> x = 0.0004932
> y = log10(x)
> z = ceiling(y)
> a = round(10^(y-z),1)
> finally = a*10^(z)
> finally
[1] 5e-04
Au risque d'être étiqueté comme un wacko complet, j'annoncerais avec joie que regexp
est votre ami. Convertissez votre numéro en chaîne de caractères, recherchez l'emplacement du premier caractère qui n'est ni "." ni "0", prenez le caractère à cet endroit et le caractère suivant derrière, convertissez-les en nombre, tour, et (parce que vous avez fait attention), multipliez le résultat par 10 $ ^ {- (nombre de zéros que vous avez trouvés entre " . "et le premier numéro)} $
var value = 0.000000564;
int cnt = 0;
bool hitNum = false;
var tempVal = value;
while (!hitNum)
{
if(tempVal > 1)
{
hitNum = true;
}
else
{
tempVal *= 10;
cnt++;
}
}
var newValue = (decimal)Math.Round(value, cnt);
Une autre approche
decimal RoundToFirstNonNullDecimal(decimal value)
{
var nullDecimals = value.ToString().Split('.').LastOrDefault()?.TakeWhile(c => c == '0').Count();
var roundTo = nullDecimals.HasValue && nullDecimals >= 1 ? nullDecimals.Value + 1 : 2;
return Math.Round(value, roundTo);
}
Résultat
Console.WriteLine(RoundToFirstNonNullDecimal(0.001m)); 0.001
Console.WriteLine(RoundToFirstNonNullDecimal(0.00367m)); 0.004
Console.WriteLine(RoundToFirstNonNullDecimal(0.000000564m)); 0.0000006
Console.WriteLine(RoundToFirstNonNullDecimal(0.00000432907543029m)); 0.000004
Console.WriteLine(RoundToFirstNonNullDecimal(0.12m)); 0.12
Console.WriteLine(RoundToFirstNonNullDecimal(1.232m)); 1.23
Console.WriteLine(RoundToFirstNonNullDecimal(7)); 7.00