I want to shorten a number to the first significant digit that is not 0. The digits behind should be rounded.
Examples:
0.001 -> 0.001
0.00367 -> 0.004
0.00337 -> 0.003
0.000000564 -> 0.0000006
0.00000432907543029 -> 0.000004
Currently I have the following procedure:
if (value < (decimal) 0.01)
{
value = Math.Round(value, 4);
}
Note:
As you can see from the examples above, a rounding to 4 Decimal places might not be enough and the value might vary greatly.
Rounding to zero decimal places means rounding so there are no numbers after the decimal point, which means rounding to the nearest 1. In the previous example of 362.715, rounding to 0 decimal places would mean rounding to the nearest 1. So, 362.715 → 363.000 → 363.
When you round to the first decimal place, or to the nearest tenth, the number in the hundredth point place will determine whether you round up or down. If the hundredths digit is a number between 5 and 9, round up to the nearest whole number.
There are certain rules to follow when rounding a decimal number. Put simply, if the last digit is less than 5, round the previous digit down. However, if it's 5 or more than you should round the previous digit up. So, if the number you are about to round is followed by 5, 6, 7, 8, 9 round the number up.
I would declare precision
variable and use a iteration multiplies that variable by 10
with the original value it didn't hit, that precision
will add 1
.
then use precision
variable be Math.Round
second parameter.
I had added some modifications to the method which can support not only zero decimal points but also all decimal numbers.
static decimal RoundFirstSignificantDigit(decimal input) {
if(input == 0)
return input;
int precision = 0;
var val = input - Math.Round(input,0);
while (Math.Abs(val) < 1)
{
val *= 10;
precision++;
}
return Math.Round(input, precision);
}
I would write an extension method for this function.
public static class FloatExtension
{
public static decimal RoundFirstSignificantDigit(this decimal input)
{
if(input == 0)
return input;
int precision = 0;
var val = input - Math.Round(input,0);
while (Math.Abs(val) < 1)
{
val *= 10;
precision++;
}
return Math.Round(input, precision);
}
}
then use like
decimal input = 0.00001;
input.RoundFirstSignificantDigit();
c# online
Result
(-0.001m).RoundFirstSignificantDigit() -0.001
(-0.00367m).RoundFirstSignificantDigit() -0.004
(0.000000564m).RoundFirstSignificantDigit() 0.0000006
(0.00000432907543029m).RoundFirstSignificantDigit() 0.000004
Something like that ?
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);
}
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);
code is from R
but the algo should be obvious
> x = 0.0004932
> y = log10(x)
> z = ceiling(y)
> a = round(10^(y-z),1)
> finally = a*10^(z)
> finally
[1] 5e-04
At the risk of being labelled a complete wacko, I would cheerfully announce that regexp
is your friend. Convert your number to a char string, search for the location of the first char that is neither "." nor "0" , grab the char at that location and the next char behind it, convert them to a number, round, and (because you were careful), multiply the result by $10^{-(number of zeros you found between "." and the first number)}$
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With