Decimal Places

Cookieman

BSOD Kernel Dump Senior Analyst
Joined
Jun 21, 2012
Posts
124
Location
Lincoln (UK)
Hi All :wave:

So far all is going well in my little program! One thing I am stuck on however is decimal places without any rounding. Below is a screenshot of my Application Console program (as yet incomplete!) with realistic values that I will be using as input. One very important aspect of this program is the average which is shown under Calculation of Control Limits. In this instance is it presently at 0.08875...blah blah blah.

What I am interested in, is the first two numbers after the decimal place, in this case it is 08. This number is then tripled to create what we call the Cpk Division Factor. If we take 0.08 and times it by 3 we get an answer of 0.24, however, under the current circumstances, the program is multiplying the whole of the number and returning an answer of 0.266272..... and so on which gives me a discrepancy of 0.02 after the first two decimal places which is something I do not want to happen.

What I need to know is simply how to get c# to read just the first two places after the point and then multiply it. I have included my code for the program below (although it may seem messy and full of holes (something to refine later) to the experts here!)

2012-11-04_150214.png


Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Averages
{
    class Program
    {
        static void Main(string[] args)
        {

            // Welcome to my first program! - Averages

            Console.WriteLine("****************************************");
            Console.WriteLine("**                                    **");
            Console.WriteLine("**  Written in C Sharp by S.Percival  **");
            Console.WriteLine("**                                    **");
            Console.WriteLine("**            04/11/2012              **");
            Console.WriteLine("**                                    **");
            Console.WriteLine("****************************************");
            Console.WriteLine(" ");
            
            // Set variables

            double xr, a2, d4;

                xr=1.69;
                a2=1.02;
                d4=2.57;
            
            // Floating variables

            double nom, est, avr, tol, a, b, c, d, e, f, g, h, left, right, value;

            // Nominal Length
            Console.WriteLine("Enter Product Nominal");
            nom = Convert.ToSingle(Console.ReadLine());

            // Actual Length
            Console.WriteLine("Enter Actual Length"); 
            est = Convert.ToSingle(Console.ReadLine());

            // Average Range
            Console.WriteLine("Enter Average Range");
            avr = Convert.ToSingle(Console.ReadLine());

            // Tolerance
            Console.WriteLine("Enter Tolerance");
            tol = Convert.ToSingle(Console.ReadLine());

            // Mathematics

            a = nom + tol;
            b = nom - tol;
            c = d4 * avr;
            d = avr / xr;
            e = d * 3;
            f = d * 4;
            g = a - est;
            h = est - b;

            // Output to Screen            

            Console.WriteLine(" ");
            Console.WriteLine("Product Information.......");
            Console.WriteLine(" ");
            Console.WriteLine("Upper Limit (UCLx) = " + a);
            Console.WriteLine("Lower Limit (LCLx) = " + b);
            Console.WriteLine("Lower Range (LCLr) = " + c);
            Console.WriteLine(" ");
            Console.WriteLine("Calculation of Control Limits.......");
            Console.WriteLine(" ");
            Console.WriteLine("Average = " + d);
            Console.WriteLine("CPK Division Factor = " + e);
            Console.WriteLine("Estimate of Capability " + f);
            Console.WriteLine(" ");
            Console.WriteLine("Cpk Values.......");
            Console.WriteLine(" ");
            Console.WriteLine("Left Cpk = " +g);
            Console.WriteLine("Right Cpk = " +h);
            Console.WriteLine(" ");
            Console.WriteLine("Press any key to exit");
            Console.ReadLine();                     
        }
    }
}

Thanks.
 
Okay, so looking at this number: 0.08875...

For the first 2 decimal places, you want that to be an unrounded value correct? Thus 0.08 instead of 0.09? Usually the rounding comes after the calculation, but i'll take your word for this because I have no idea what kind of calculation you're doing and the prerequisites for the numeric values involved...

This was an issue on MSDN to somebody I helped out, so i'll show you what I came up with as well for unrounded values. We can't deal with this number and dumb it down to 2 decimal places as it stands, so a string manipulation is the best way here.

Code:
double d = 0.0887573999766062;
d = Fix(d, 2); //Fix d to 2 decimal places = 0.08
double result = d * 3;
Console.WriteLine(result); //Writes 0.24

Here's my function:
Code:
private double Fix(double d, int places)
{
	string s = d.ToString();
	if (s.Substring(s.IndexOf('.') + 1).Length <= places)
		return double.Parse(d.ToString("0." + new string(Enumerable.Repeat('0', places).ToArray())));
	string pattern = @"[0-9]+(\.[0-9]+)";
	if (Regex.IsMatch(s, pattern))
	{
		string decimals = Regex.Match(s, pattern).Groups[1].Value;
		return double.Parse(s.Replace(decimals, decimals.Substring(0, places + 1)));
	}

	return 0.0;
}

For Regex, you'll need to add this line at the top:
Code:
using System.Text.RegularExpressions;
 
Last edited:
Hi Ace

you want that to be an unrounded value correct?

Yes that is definitely the case here, although the workaround is much more complicated that I expected! I do understand the first part of code (please bear in mind I am a beggininer!) But I will need to sit and digest the second part of the code which you have generously offered as a workaround. I do like to understand what I am doing rather than just place a section of code in!

I did originally think I would be able to convert from float to decimal to get around the problem. I did try to change the program so the user input as in the form of decimal but that did not work either.

Just as a side question, is it a case of using the right tools for the job ie; another language would suit this program better, or would it be the same no matter what language is used.

Regards...
 
Hi Ace

you want that to be an unrounded value correct?

Yes that is definitely the case here, although the workaround is much more complicated that I expected! I do understand the first part of code (please bear in mind I am a beggininer!) But I will need to sit and digest the second part of the code which you have generously offered as a workaround. I do like to understand what I am doing rather than just place a section of code in!

I did originally think I would be able to convert from float to decimal to get around the problem. I did try to change the program so the user input as in the form of decimal but that did not work either.

Just as a side question, is it a case of using the right tools for the job ie; another language would suit this program better, or would it be the same no matter what language is used.

Regards...

In my honest opinion, it doesn't matter what language you use. The programmer is only as good as well as he knows his own language. It doesn't make you any better of a programmer just because you're programming in C++ vs. Batch even. You still face the learning curves...

80% of it depends on the standard libraries given to you for a specific language, or methods to be as general as I can be here; it's a different name depending on the language, powershell would refer to cmdlets and such... But to the point, numbers are numbers. 32 bit, 64 bit, 16 bit, signed, unsigned, integer, decimal, etc... What is given to you in the form of libraries, like C# with the .NET framework, decides how we deal with these numbers if we choose to use the common libraries that most would use. In some cases, and ALL languages have some kind of thing that would require a work-around, you have to make your own methodology to deal with numeric value types, just like I did here.

C# is well capable of what you're trying to do here. I gawk at the programming language vs programming language debates though for several reasons. To each his own, and it really depends on the limitations. Low level vs. high level programming languages, compilers, etc...

You may want to make a boot utility, in which case you'll need a compiler that can compile to 16 bit. You may want some program that deals with the kernel on a low level, in which case maybe C or C++, but other than that, who decides that any language is better than another?

Which exact parts of my method do you not fully grasp? I'll try to help you.
 
Hi Ace

I have finally found time to sit down and digest a few things once again (its been a busy week!) Once again, many thanks for your reply and your patience.

C# is well capable of what you're trying to do here.

I have no doubt of that. It's just a case of me understanding it. The only thing I have done prior to this is hello world!. As mentioned I like to understand what I am doing, and to see programming script in such form it can seem a bit daunting at first. Maybe I am trying to jump ahead to fast and need to slow back down a touch.

Which exact parts of my method do you not fully grasp? I'll try to help you.

I can understand what the first part is doing.

Code:
double d = 0.0887573999766062; 
d = Fix(d, 2); //Fix d to 2 decimal places = 0.08 
double result = d * 3; 
Console.WriteLine(result); //Writes 0.24

Its the second peice of code I am looking to understand. This is the part I do not fully understand. From what I can make out it is parsing a double to string, then converting it to an array?

Code:
return double.Parse(d.ToString("0." + new string(Enumerable.Repeat('0', places).ToArray()))); 
 string pattern = @"[0-9]+(\.[0-9]+)";
 
Maybe I am trying to jump ahead to fast and need to slow back down a touch.

In my honest opinion, numbers are easy concept-wise. You just weren't aware that what you were doing with numeric manipulation was as difficult as it was, easy first mistake.

I'd suggest you keep going in the direction you're headed. However overwhelming it may be at first, the more you see, the more you may understand if you come across certain code issues that help you learn after a bit of troubleshooting.

Its the second piece of code I am looking to understand. This is the part I do not fully understand. From what I can make out it is parsing a double to string, then converting it to an array?

Close, but not quite. It's important to see where the brackets match up.

We've got:
Code:
return double.Parse();

And inside of the Parse() function, we've got:
Code:
d.ToString("0." + new string(Enumerable.Repeat('0', places).ToArray()))

And inside of the ToString() method, we've got our string formatting data. More information on String formatting here: http://msdn.microsoft.com/en-us/library/0c899ak8.aspx

The String formatting:
Code:
"0." + new string(Enumerable.Repeat('0', places).ToArray())

2 Parts
1) "0."
2) new string(Enumerable.Repeat('0', places).ToArray())

In the end, these both evaluate to this kind of format, depending on the second part:

-"0.0"
-"0.00"
-"0.000"
-etc...

In that second part we have:
Code:
new string(Enumerable.Repeat('0', places).ToArray())

Here, it's important to see that we are using a String class constructor here that takes a Char array as an input argument. The only thing is... Enumerable.Repeat returns an IEnumerable result, and not a String array, so we need to cast that to an Array using the ToArray() function.

Enumerable.Repeat takes the '0' and repeats it however many times specified by the places variable as our second argument for that function.

So we end up with a repeated number of '0's that we cast to an array of char elements of '0', and when we give this to the constructor for the String class which takes a char array as an input argument, it takes this Char array, and combines it into a single string.

So say, places was a value of 3.

We would have "0." and the Repeated result of 3 '0's which would give us "0." + "000", and thus "0.000" which would format our decimal value to 3 decimal places if you understand the string formatting.

More information on the String constructor overload I was using here: http://msdn.microsoft.com/en-us/library/ttyxaek9.aspx
 
So am I right in thinking that

Code:
d.ToString("0." + new string(Enumerable.Repeat('0', places).ToArray()))

Would result in the total showing without decimal places (like an interger) and

Code:
d.ToString("0." + new string(Enumerable.Repeat('0.00', places).ToArray()))

Would result in the totals showing as floated to 2 decimal places?
 
So am I right in thinking that

Code:
d.ToString("0." + new string(Enumerable.Repeat('0', places).ToArray()))

Would result in the total showing without decimal places (like an interger) and

Code:
d.ToString("0." + new string(Enumerable.Repeat('0.00', places).ToArray()))

Would result in the totals showing as floated to 2 decimal places?

No. Remember, and/or think, String Concatenation to build our string format.

We already have the "0." in the front:
Code:
d.ToString("0."....

Then to finish off before we close the bracket to end the ToString() function, we use Enumerable.Repeat()

Enumerable.Repeat({First Param}, {Second Param}) takes the first param, and repeats it by n times, indicated by the value of the second param as an integer. In the case above, it's indicated to repeat, by however many times, indicated by the places variable. And we repeat the '0' character. This repetition gets concatenated with our "0."

So a repetition of 3 '0's from the Enumerable.Repeat function would concatenate "0." and 3 0's which would be: "0.000"

We end up with:

Code:
d.ToString("0.000")

In that case... As an example.

This:
Code:
d.ToString("0." + new string(Enumerable.Repeat('0.00', places).ToArray()))

Would not work because '0.00' is not a char value, that would be a string value, and would need to be wrapped in double quotes instead of single quotes. Single quotes are for chars in C#, double quotes are for string values.

Lets take a step through a full example:
Code:
new string(Enumerable.Repeat('0', 3).ToArray()))

Enumerable.Repeat('0', 3) - Would return an IEnumerable<Char>, consisting of 3 '0' elements.

Enumerable.Repeat('0', 3).ToArray() - Casts that IEnumerable<Char> to a Char[] (Character array) which would look something like this: {'0', '0', '0'}

Now imagine that Char[] (Char array) being placed inside of new String():
new String(new char[] {'0', '0', '0'}) - This takes each element ('0') from that array, and turns it into a String value: "000"

Now we have:
d.ToString("0." + "000") - Which gives us: d.ToString("0.000")

Which formats our double, to a string value with 3 decimal places.
 
Actually, look into the Math.Truncate method too. Forgot about that. This could be achieved using that as well with some type casting.
 
Thanks you Ace - The information is finally penetrating the wood between the ears! Im beginning to see a little clearer now...

My interpretation of a broken down version of events...

return double.Parse(d.ToString ("0." + new string (Enumerable.Repeat ('0', places).ToArray()) ) );

And for example

Code:
new string(Enumerable.Repeat('0', 5).ToArray()))

would be turned in to an array of {'0', '0', '0', '0', '0'} which is then stripped back down by removing ' ' with the new String to provide 5 zero's

whilst d is typecasted (nominated by d.ToString) then added prior to the point here - ("0." which in this case would result in this case 5 decimal places
 
Seems about right :) Yes

The most important thing is to see where the brackets match up, so that coloring is good that you've done. This in its entirety:
Code:
("0." + new string (Enumerable.Repeat ('0', places).ToArray()) )

Is our string format for the ToString() function called from variable d.

That entire thing gets evaluated first in order to decide the string format used by the ToString() method so that d can be cast to a String, in the format that we want it to be in.

My apologies though, I was thinking about Math.Truncate before I remembered what kind of numbers you were dealing with. Math.Truncate takes the whole value of a number only, as it's unrounded representative, meaning it just reduces the decimal portion down to 0. This wouldn't work here as you are dealing with numbers < 0... It only sparked my brain because I remembered it's unrounded feature.
 
Last edited:

Has Sysnative Forums helped you? Please consider donating to help us support the site!

Back
Top