Finding the number of places after the decimal point of a Double - c#

I have a Double value:
double a = 4.5565;
What is the easiest way to calculate the number of digits after the decimal point (4 in this case).
I know that I can convert to string and do a split and take the length. But is there an easier way?

There's no easy way, especially since the number of digits mathematically speaking might be far more than displayed. For example, 4.5565 is actually stored as 4.556499999999999772626324556767940521240234375 (thanks to harold for calculating that). You're very unlikely to find a useful solution to this problem.
EDIT
You could come up with some algorithm that works like this: if, as you calculate the decimal representation, you find a certain number of 9s (or zeros) in succession, you round up (or down) to the last place before the series of 9s (or zeros) began. I suspect that you would find more trouble down that road than you would anticipate.

var precision = 0;
var x = 1.345678901m;
while (x*(decimal)Math.Pow(10,precision) !=
Math.Round(x*(decimal)Math.Pow(10,precision)))
precision++;
precision will be equal to the number of significant digits of the decimal value (setting x to 1.23456000 will result in a precision of 5 even though 8 digits were originally specified in the literal). This executes in time proportional to the number of decimal places. It counts the number of fractional digits ONLY; you can count the number of places to the left of the decimal point by taking the integer part of Math.Log10(x). It works best with decimals as they have better value precision so there is less rounding error.

Write a function
int CountDigitsAfterDecimal(double value)
{
bool start = false;
int count = 0;
foreach (var s in value.ToString())
{
if (s == '.')
{
start = true;
}
else if (start)
{
count++;
}
}
return count;
}

I think this might be a solution:
private static int getDecimalCount(double val)
{
int i=0;
while (Math.Round(val, i) != val)
i++;
return i;
}
double val9 = 4.5565d; int count9 = getDecimalCount(val9);//result: 4
Sorry for the duplication -> https://stackoverflow.com/a/35238462/1266873

base on james answer bat much clearer:
int num = dValue.ToString().Length - (((int)dValue).ToString().Length + 1);
num is the exact number of digits after the decimal point.
without including 0 like this(25.520000)
in this case, you will get num= 2

I Think String solution is best : ((a-(int)a)+"").length-2

I'll perhaps use this code if I needed,
myDoubleNumber.ToString("R").Split('.')[1].Length
"R" here is Round Trip Format Specifier
We need to check for the index bounds first of course.

Another solution would be to use some string functions:
private int GetSignificantDecimalPlaces(decimal number, bool trimTrailingZeros = true)
{
var stemp = Convert.ToString(number);
if (stemp.IndexOf(Application.CurrentCulture.NumberFormat.NumberDecimalSeparator) < 0)
return 0;
if (trimTrailingZeros)
stemp = stemp.TrimEnd('0');
return stemp.Length - 1 - stemp.IndexOf(Application.CurrentCulture.NumberFormat.NumberDecimalSeparator);
}
Remember to use System.Windows.Forms to get access to Application.CurrentCulture

Related

Counting Precision Digits

How to count precision digits on a C# decimal type?
e.g. 12.001 = 3 precision digits.
I would like to thrown an error is a precision of greater than x is present.
Thanks.
public int CountDecPoint(decimal d){
string[] s = d.ToString().Split('.');
return s.Length == 1 ? 0 : s[1].Length;
}
Normally the decimal separator is ., but to deal with different culture, this code will be better:
public int CountDecPoint(decimal d){
string[] s = d.ToString().Split(Application.CurrentCulture.NumberFormat.NumberDecimalSeparator[0]);
return s.Length == 1 ? 0 : s[1].Length;
}
You can get the "scale" of a decimal like this:
static byte GetScale(decimal d)
{
return BitConverter.GetBytes(decimal.GetBits(d)[3])[2];
}
Explanation: decimal.GetBits returns an array of four int values of which we take only the last one. As described on the linked page, we need only the second to last byte from the four bytes that make up this int, and we do that with BitConverter.GetBytes.
Examples: The scale of the number 3.14m is 2. The scale of 3.14000m is 5. The scale of 123456m is 0. The scale of 123456.0m is 1.
If the code may run on a big-endian system, it is likely that you have to modify to BitConverter.GetBytes(decimal.GetBits(d)[3])[BitConverter.IsLittleEndian ? 2 : 1] or something similar. I have not tested that. See the comments by relatively_random below.
I know I'm resurrecting an ancient question, but here's a version that doesn't rely on string representations and actually ignores trailing zeros. If that's even desired, of course.
public static int GetMinPrecision(this decimal input)
{
if (input < 0)
input = -input;
int count = 0;
input -= decimal.Truncate(input);
while (input != 0)
{
++count;
input *= 10;
input -= decimal.Truncate(input);
}
return count;
}
I would like to thrown an error is a precision of greater than x is present
This looks like the simplest way:
void AssertPrecision(decimal number, int decimals)
{
if (number != decimal.Round(number, decimals, MidpointRounding.AwayFromZero))
throw new Exception()
};

custom method for returning decimal places shows odd behavior

I am writing a simple method that will calculate the number of decimal places in a decimal value. The method looks like this:
public int GetDecimalPlaces(decimal decimalNumber) {
try {
int decimalPlaces = 1;
double powers = 10.0;
if (decimalNumber > 0.0m) {
while (((double)decimalNumber * powers) % 1 != 0.0) {
powers *= 10.0;
++decimalPlaces;
}
}
return decimalPlaces;
I have run it against some test values to make sure that everything is working fine but am getting some really weird behavior back on the last one:
int test = GetDecimalPlaces(0.1m);
int test2 = GetDecimalPlaces(0.01m);
int test3 = GetDecimalPlaces(0.001m);
int test4 = GetDecimalPlaces(0.0000000001m);
int test5 = GetDecimalPlaces(0.00000000010000000001m);
int test6 = GetDecimalPlaces(0.0000000001000000000100000000010000000001000000000100000000010000000001000000000100000000010000000001m);
Tests 1-5 work fine but test6 returns 23. I know that the value being passed in exceeds the maximum decimal precision but why 23? The other thing I found odd is when I put a breakpoint inside the GetDecimalPlaces method following my call from test6 the value of decimalNumber inside the method comes through as the same value that would have come from test5 (20 decimal places) yet even though the value passed in has 20 decimal places 23 is returned.
Maybe its just because I'm passing in a number that has way too many decimal places and things go wonky but I want to make sure that I'm not missing something fundamentally wrong here that might throw off calculations for the other values later down the road.
The number you're actually testing is this:
0.0000000001000000000100000000
That's the closest exact decimal value to 0.0000000001000000000100000000010000000001000000000100000000010000000001000000000100000000010000000001.
So the correct answer is actually 20. However, your code is giving you 23 because you're using binary floating point arithmetic, for no obvious reason. That's going to be introducing errors into your calculations, completely unnecessarily. If you change to use decimal consistently, it's fine:
public static int GetDecimalPlaces(decimal decimalNumber) {
int decimalPlaces = 1;
decimal powers = 10.0m;
if (decimalNumber > 0.0m) {
while ((decimalNumber * powers) % 1 != 0.0m) {
powers *= 10.0m;
++decimalPlaces;
}
}
return decimalPlaces;
}
(Suggestion) You could calculate that this way:
public static int GetDecimalPlaces(decimal decimalNumber)
{
var s = decimalNumber.ToString();
return s.Substring(s.IndexOf(CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator) + 1).Length;
}
There is another way to do this and probably it works faster because it uses remainder operation only if the decimal number has a "trailing zeros" problem.
The basic idea:
In .NET any decimal is stored in memory in the form
m * Math.Power(10, -p)
where m is mantissa (96 bit size) and p is order (value from 0 to 28).
decimal.GetBits method retrieves this representation from decimal struct and returns it as array of int (of length 4).
Using this data we can construct another decimal. If we will use only mantissa, without "Math.Power(10, -p)" part, the result will be an integral decimal. And if this integral decimal number is divisible by 10, then our source number has one or more trailing zeros.
So here is my code
static int GetDecimalPlaces(decimal value)
{
// getting raw decimal structure
var raw = decimal.GetBits(value);
// getting current decimal point position
int decimalPoint = (raw[3] >> 16) & 0xFF;
// using raw data to create integral decimal with the same mantissa
// (note: it always will be absolute value because I do not analyze
// the sign information of source number)
decimal integral = new decimal(raw[0], raw[1], raw[2], false, 0);
// disposing from trailing zeros
while (integral > 0 && integral % 10 == 0)
{
decimalPoint--;
integral /= 10;
}
// returning the answer
return decimalPoint;
}

How to make it so trailing zeros are not dropped in a decimal number in C#?

I'm creating a RandomDecimal function that allows you to specify a max value and the precision of the decimal. However, if the last random digit that is chosen happens to be zero, it is dropped and the precision is off. Is it possible to not lose this zero? I tried converting it to a string then back to a decimal but it's still dropped.
public static decimal RandomDecimal(int maxValue, int precision)
{
decimal result = new Random().Next(maxValue);
if (maxValue == 0)
{
return result;
}
int i = 1;
decimal digit = 0;
while (i <= precision)
{
digit = new Random().Next(10);
digit = (digit / (Convert.ToDecimal(Math.Pow(10, i))));
result = result + digit;
i++;
}
// if (digit == 0)
// {
// string resultstring = Convert.ToString(result) + '0';
// result = Convert.ToDecimal(resultstring);
// } This code doesn't do anything because the zero is still dropped.
return result;
}
If you want a representation of a number that has not only a value, but knows how many significant digits it has you are better off using something other than double/float/decimal. You'll want to create your own type that has both a value and a SignificantDigits property.
You simply want information decimal wasn't designed to have users using. In certain situations (where you have no leading/trailing zeros) you coincidentally end up with the same result despite [decimal] not knowing the information, but that's irrelevant to you solving your problem.
What you want can only be done (with built-in types) by returning a string instead of a decimal. You should also declare a Random object once, not creating new ones multiple times within a single method.
static Random r = new Random();
public static string RandomDecimal(int maxValue, int precision)
{
decimal result = r.Next(maxValue);
if (maxValue != 0)
{
int i = 1;
decimal digit = 0;
while (i <= precision)
{
digit = r.Next(10);
digit = (digit / (Convert.ToDecimal(Math.Pow(10, i))));
result = result + digit;
i++;
}
}
var sb = new StringBuilder("0.");
sb.Append('0', precision);
return result.ToString(sb.ToString());
}
The Decimal struct stores a scaling factor internally as a power of 10. According to MSDN,
The scaling factor also preserves any trailing zeroes in a Decimal
number. Trailing zeroes do not affect the value of a Decimal number in
arithmetic or comparison operations. However, trailing zeroes can be
revealed by the ToString method if an appropriate format string is
applied.
It's not obvious to me that there's any way to directly set the scaling factor, except by using the Decimal (Int32, Int32, Int32, Boolean, Byte) constructor, which takes the scaling factor as the last argument. So, in theory, you could create a decimal with significant trailing 0s by creating your Decimals with that constructor.

Performing Math operations on decimal datatype in C#?

I was wondering if the above was at all possible. For example:
Math.Sqrt(myVariableHere);
When looking at the overload, it requires a double parameter, so I'm not sure if there is another way to replicate this with decimal datatypes.
I don't understand why all the answers to that question are the same.
There are several ways to calculate the square root from a number. One of them was proposed by Isaac Newton. I'll only write one of the simplest implementations of this method. I use it to improve the accuracy of double's square root.
// x - a number, from which we need to calculate the square root
// epsilon - an accuracy of calculation of the root from our number.
// The result of the calculations will differ from an actual value
// of the root on less than epslion.
public static decimal Sqrt(decimal x, decimal epsilon = 0.0M)
{
if (x < 0) throw new OverflowException("Cannot calculate square root from a negative number");
decimal current = (decimal)Math.Sqrt((double)x), previous;
do
{
previous = current;
if (previous == 0.0M) return 0;
current = (previous + x / previous) / 2;
}
while (Math.Abs(previous - current) > epsilon);
return current;
}
About speed: in the worst case (epsilon = 0 and number is decimal.MaxValue) the loop repeats less than a three times.
If you want to know more, read this (Hacker's Delight by Henry S. Warren, Jr.)
I just came across this question, and I'd suggest a different algorithm than the one SLenik proposed. This is based on the Babylonian Method.
public static decimal Sqrt(decimal x, decimal? guess = null)
{
var ourGuess = guess.GetValueOrDefault(x / 2m);
var result = x / ourGuess;
var average = (ourGuess + result) / 2m;
if (average == ourGuess) // This checks for the maximum precision possible with a decimal.
return average;
else
return Sqrt(x, average);
}
It doesn't require using the existing Sqrt function, and thus avoids converting to double and back, with the accompanying loss of precision.
In most cases involving a decimal (currency etc), it isn't useful to take a root; and the root won't have anything like the expected precision that you might expect a decimal to have. You can of course force it by casting (assuming we aren't dealing with extreme ends of the decimal range):
decimal root = (decimal)Math.Sqrt((double)myVariableHere);
which forces you to at least acknowledge the inherent rounding issues.
Simple: Cast your decimal to a double and call the function, get the result and cast that back to a decimal. That will probably be faster than any sqrt function you could make on your own, and save a lot of effort.
Math.Sqrt((double)myVariableHere);
Will give you back a double that's the square root of your decimal myVariableHere.

Dynamic number format in .NET?

I have a problem and cant find a solution. I have numbers (decimal) like 85.12343 or 100 or 1.123324. I want to format this in a way that the result is always 13 chars long including the separator.
100 --> 100.000000000
1.123324 --> 1.12332400000
I tried with toString, but failed. How could I do this?
Thanks :)
int digits = 13;
decimal d = 100433.2414242241214M;
int positive = Decimal.Truncate(d).ToString().Length;
int decimals = digits - positive - 1; //-1 for the dot
if (decimals < 0)
decimals = 0;
string dec = d.ToString("f" + decimals);
It will not remove digits from the whole part, only the fraction, when needed.
I'd go with Kobi's answer, unless it's possible you could have more than 13 digits to start with, in which case you might need to do something like this (warning: I have not even attempted to make this efficient; surely there are ways it could be optimized if necessary):
public static string ToTrimmedString(this decimal value, int numDigits)
{
// First figure out how many decimal places are to the left
// of the decimal point.
int digitsToLeft = 0;
// This should be safe since you said all inputs will be <= 100M anyway.
int temp = decimal.ToInt32(Math.Truncate(value));
while (temp > 0)
{
++digitsToLeft;
temp /= 10;
}
// Then simply display however many decimal places remain "available,"
// taking the value to the left of the decimal point and the decimal point
// itself into account. (If negative numbers are a possibility, you'd want
// to subtract another digit for negative values to allow for the '-' sign.)
return value.ToString("#." + new string('0', numDigits - digitsToLeft - 1));
}
Example inputs/output:
Input Output
---------------------------------------
100 100.000000000
1.232487 1.23248700000
1.3290435309439872321 1.32904353094
100.320148109932888473 100.320148110
0.000383849080819849081 .000383849081
0.0 .000000000000
Quick 'n' dirty:
return (value.ToString("0.#") + "0000000000000").Substring(0, 13);
string formatted = original.ToString("0.000000000000").Remove(13);
Besides simply padding the string you can do some more elaborate math to determine the number of digits:
String FormatField(Int32 fieldWidth, Decimal value) {
var integerPartDigits =
value != Decimal.Zero ? (int) Math.Log10((Double) value) + 1 : 1;
var fractionalPartDigits = Math.Max(0, fieldWidth - integerPartDigits - 1);
return value.ToString("F" + fractionalPartDigits);
}
Note that if the value is negative or has an integer part with one less digit than the field width you will not get the desired result. However, you can modify the code to accommodate these cases based on exactly how you want to format and align these numbers.
What about
string newString;
if (original.ToString().Length >= 13)
{
newString = original.ToString().Substring(13);
}
else
{
newString = original.ToString().PadRight(13, '0');
}
int noofdecimal=3;
double value=1567.9800
value.ToString("#." + new string('0', noofdecimal));
//Result=1567.980

Categories

Resources