0

I have 4 examples:

double a = 1.05;
double b = 0.000056;
double c = 0.7812;
double d = 1.2;

What I want to do is first find how many place values there are. in this case

int inta = 2;
int intb = 6;
int intc = 4;
int intd = 1;

Then I want to create a string with "0" representing those digits. this is for a ToString()

string stra = ".00";
string strb = ".000000";
string strc = ".0000";
string strd = ".0";

So the only thing I have is a double. I need the place value, then how to create the string.

lakersfan
  • 71
  • 5
  • How about: convert the numbers to a string of a very long length (like 10 decimal positions). Then delete all leading digits (until the dot). Then delete all trailing zeros. – Flydog57 May 08 '20 at 02:51
  • @Flydog57 Problem with that is that different values `a != b` can have the same string representation, example [below](https://stackoverflow.com/questions/61671134/how-do-i-covert-the-decimal-places-of-a-double-to-a-string/61671210?noredirect=1#comment109089960_61671301), and this happens even if you look at 17 digits which is the maximum number of reliable significant digits for the `double` type. – dxiv May 08 '20 at 06:18
  • I read "decimal" in the title and had `decimal` in my head. Yeah, nothing's going to work with with `double` – Flydog57 May 08 '20 at 14:27

3 Answers3

4

You could convert the double to a string, then get the substring:

double a = 1.05;

// Convert to string
string aString = a.ToString();

// Get substring
string result = aString.Substring(aString.LastIndexOf('.'));

// Get number of decimals
int numDecimals = result.Length - 1;

// Create string based on decimal count
string zeroString = ".";
for (int i = 0; i < numDecimals; i++) {
   zeroString += "0";
}

Console.WriteLine(result);
Console.WriteLine(numDecimals);
Console.WriteLine(zeroString);
// .05
// 2
// .00

** To ensure this works for all cultures and not only those who utilize '.' as the decimal separator, you could replace:

LastIndexOf('.')

with

LastIndexOf(System.Threading.Thread.CurrentThread.CurrentCulture.NumberFormat.CurrencyDecimalSeparator)

(Thanks @John)

retryW
  • 663
  • 3
  • 8
  • This won't work for all cultures! – jason.kaisersmith May 08 '20 at 03:11
  • Thanks this is helpful. How do then create a new string with ".00"? – lakersfan May 08 '20 at 03:12
  • 2
    @jason.kaisersmith , no it won't. However judging by the question I presume his code will only be running under cultures containing a '.' for decimals. I've edited my answer to contain a warning – retryW May 08 '20 at 03:17
  • 1
    Rather than hard-coding it, when not use `System.Threading.Thread.CurrentThread.CurrentCulture.NumberFormat.CurrencyDecimalSeparator` or `Sytem.Globalization.CultureInfo.CurrentCulture.NumberFormat.CurrencyDecimalSeparator`? – ProgrammingLlama May 08 '20 at 03:21
  • @lakersfan I have updated the answer to include the ".00" string – retryW May 08 '20 at 03:22
  • @John I have updated the answer to include your compatibility suggestion – retryW May 08 '20 at 03:27
  • @retryW This will work most of the time, but not all the time (and in fact *cannot* work all the time, as I argued in my post). Change the first line to `double a = 0.2999999999999999;` for example, and you'll get `result = .3` and numDecimals = 1` even though `a == 0.3` evaluates to `false`. – dxiv May 08 '20 at 06:06
0

Doubles don't represent all base-10 fractions accurately. Instead you should use the decimal type.

decimal a = 1.05;
decimal b = 0.000056;
decimal c = 0.7812;
decimal d = 1.2;
decimal e = 1.456000;

a.ToString() == "1.05"
b.ToString() == "0.000056"
c.ToString() == "0.7812"
d.ToString() == "1.2"
e.ToString() == "1.456000"

public static decimal Normalise(this decimal value) => value / 1.000000000000000000000000000000000m;

e.Normalise().ToString() == "1.456"
Ray
  • 2,713
  • 3
  • 29
  • 61
Jeremy Lakeman
  • 9,515
  • 25
  • 29
  • Right. For example, (double)0.1m == 3602879701896397 / 36028797018963968. – Jeremy Lakeman May 08 '20 at 04:26
  • The point about double vs decimal may be accurate, but this does not answer the question. It should have been a comment. – Herohtar May 08 '20 at 04:46
  • @Herohtar It so happens that I just posted another (non-)answer where I contend that there exists no "right" answer to the question as stated. – dxiv May 08 '20 at 04:54
0

I am of the opinion that the problem is ill posed. Specifically, this part:

first find how many [decimal] place values there are

The number of decimal digits is a matter of representation, not an intrinsic property of the number. When writing double a = 0.3; what gets stored into variable a is the double precision value which is closest to the exact decimal 0.3. That value is close to 0.3, but not identical to 0.3 simply because IEEE-754 is binary based and 0.3 is a non-terminating binary fraction. But, once assigned, variable a has no memory of where it came from, or whether the source code had it as double a = 0.3; vs. double a = 0.29999999999999999;.

Consider the following snippet:

double a = 0.3;
double b = 0.2999999999999999;
double c = 0.29999999999999999;
Console.WriteLine("a = {0}, b = {1}, c = {2}, a==b = {3}, b==c = {4}, a==c = {5}", a, b, c, a==b, b==c, a==c);

The output is:

a = 0.3, b = 0.3, c = 0.3, a==b = False, b==c = False, a==c = True

What this shows is that variables a and c compare equal i.e. they hold the exact same value, yet one was defined with 1 decimal digit while the other one with 17 decimal digits. Point being that it does not make sense to speak of the number of decimal places associated with a floating point value because, as this example shows, the same value can have different representations with different numbers of decimal places..

As a side comment, the above also shows that b and c are different values, even though they differ only in the 17th decimal position. This is consistent with the double type having between 15 and 17 significant decimal digits, which is why the 16th and 17th digits cannot be ignored in general.

dxiv
  • 16,984
  • 2
  • 27
  • 49