I am revisiting one of my old exercises and I seem to be having problems with my logic. It is a simple console application that is supposed to calculate and display change for a given total and cash tendered. For some reason, I get an infinite loop when I input values which decimal part other than 25.
Here is a screenshot of my console application
Here is my code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace James_Archbold_A2
{
class Program
{
static void Main(string[] args)
{
double change,total,cash;
int fifties = 0, twenties = 0, tens = 0, fives = 0, ones = 0,
quarters = 0, dimes = 0, nickels = 0, pennies = 0;
Console.Write("Please enter the total: ");
total = Convert.ToDouble(Console.ReadLine());
Console.Write("Please enter the cash tendered: ");
cash = Convert.ToDouble((Console.ReadLine()));
change = CalculateChange(total, cash);
Console.WriteLine(change);
change *= 100;
while (change > 0)
{
if (change >= 5000)
{
change -= 5000;
fifties++;
Console.WriteLine(change);
}
else if (change >= 2000)
{
change -=2000;
twenties++;
Console.WriteLine(change);
}
else if (change >= 1000)
{
change -=1000;
tens++;
Console.WriteLine(change);
}
else if (change >= 500)
{
change -= 500;
fives++;
Console.WriteLine(change);
}
else if (change >= 100)
{
change -=100;
ones++;
Console.WriteLine(change);
}
else if (change >= 25)
{
change -=25;
quarters++;
Console.WriteLine(change);
}
else if (change >= 10)
{
change -=10;
dimes++;
Console.WriteLine(change);
}
else if (change >= 5)
{
change -=5;
nickels++;
Console.WriteLine(change);
}
else if (change >= 1)
{
change -=1;
pennies++;
Console.WriteLine(change);
}
}
StringBuilder sb = new StringBuilder();
sb.AppendFormat("Total: {0:c}\nCash Tendered: {1:c}" +
"\nChange Due: \nFifties: {2}\nTwenties: {3}\nTens: {4}" +
"\nFives: {5}\nOnes: {6}\nQuarters: {7}\nDimes: {8}" +
"\nNickels: {9}\nPennies: {10}",
total, cash, fifties, twenties, tens, fives, ones, quarters, dimes,nickels, pennies);
Console.WriteLine(sb);
Console.ReadLine();
}
static double CalculateChange(double total, double cash)
{
return cash - total;
}
}
}
If you're writing anything to do with financial calculation, use the decimal data-type. Not double.
I think Jon Skeet's article "Binary floating point and .NET" will give you the greatest insight.
A few other pointers:
Is a double really unsuitable for money?
What Every Computer Scientist Should Know About Floating Point
Make change an int to truncate the .0000000000001 that prevents change from ever falling below 0 with your current algorithm:
int change = CalculateChange(total, cash) * 100;
Use change>=0 in while condition
You're using doubles for your arithmetic, you can't rely on exact precision for decimal input using doubles.
If you want to use floating points with decimal, you should use C# decimal type.
In the code snippet provided just round the value of the variable change to its nearest integer and you would get the required output.
From
`while (change > 0)`
to
`while(Math.Round(change)>0)`
Hope this helps
Related
Code:
This program checks if the 2 numbers entered and their sum are divisible by the numbers 2 - 9, and displays the remaining divisible numbers (excluding the one being reviewed).
static void Main(string[] args)
{
for (int i = 2; i < 10; i++)
{
Challenge(2, 6, i);
}
Console.ReadLine();
}
static void Challenge(int num1, int num2, int Divisor)
{
int sum = num1 + num2;
bool SumDivisible = sum % Divisor == 0;
bool num1Divisible = num1 % Divisor == 0;
bool num2Divisible = num2 % Divisor == 0;
int highNum = 80;
List<int> NumbersDivisible = Enumerable.Range(1, highNum).Where(x => x % Divisor == 0).ToList();
// Use the booleans to determine output.
if (SumDivisible || num1Divisible || num2Divisible)
{
if (SumDivisible)
{
Console.WriteLine("The SUM ({0}) is divisible by {1}! \nThe remaining USABLE numbers for {1} are:\n", sum, Divisor);
outputListExceptInt(NumbersDivisible, sum);
//output
Console.WriteLine("\n\n");
}
if (num1Divisible)
{
Console.WriteLine("The FIRST number ({0}) is divisible by {1}! \nThe remaining USABLE numbers for {1} are:\n", num1, Divisor);
outputListExceptInt(NumbersDivisible, num1);
//output
Console.WriteLine("\n\n");
}
if (num2Divisible)
{
Console.WriteLine("The SECOND number ({0}) is divisible by {1}! \nThe remaining USABLE numbers for {1} are:\n", num2, Divisor);
outputListExceptInt(NumbersDivisible, num2);
//output
Console.WriteLine("\n\n");
}
}
else
{
Console.WriteLine("The NUMBERS chosen and their SUM are not divisble by {0}. \nThe USABLE numbers for {0} are:\n", Divisor);
outputListExceptInt(NumbersDivisible);
Console.WriteLine("\n\n");
}
}
public static void outputListExceptInt(List<int> NumbersDivisibleByDivisor, int except = 0)
{
var Numbers = except > 0 ? NumbersDivisibleByDivisor.Where(x => x != except) : NumbersDivisibleByDivisor;
foreach (int num in Numbers)
{
Console.WriteLine(num);
}
}
Problem:
I'm finding that when I set the range (highNum) to anything over 89, a noticeable portion from the top of the window gets cut off:
highNum = 89:
highNum = 90:
Its cutting off 6 lines just with that small jump, and I'm not sure why.
Question:
My best guess is that there must be some limit on the output that can be displayed by the Console Window. Is this correct, or is something else causing this issue?
In a console window, click on Defaults
This opens a dialog box that allows you to set the scrollback buffer size (max number of lines to retain) by default in all of your console windows.
In my screenshot it is set to 9000 because I often log output to a console, and sometimes need to be able to scroll way back.
You can also modify it from your program for the console it is running in using Console.SetBufferSize().
Yes, the console has a width and height limit. And you can change it:
Answer modified from what's given here on microsoft's website:
Click the upper-left corner of the Command Prompt window, and then click Properties.
Click the Layout tab.
In Screen Buffer Size, type or select 2500 in Height.
Save the props by pressing OK.
You just have small buffer.
So this is what happens: I enter a number and what happens is that it writes out only 1 number: 324, like the variable c is getting this value for no reason..
class Program
{
static void Main(string[] args)
{
Console.WriteLine("enter a number with change");
double num = double.Parse(Console.ReadLine());
num = (int)num;
int c = 0;
Console.WriteLine(num);
while (num != 0)
{
num /= 10;
c++;
}
Console.WriteLine(c);
}
}
This program should loop infinitely if double could represent every real number. Say you enter in 1. Then the loop will divide it by 10, leaving 0.1. Since 0.1 is not equal to 0, the loop will continue, resulting in 0.01, etc.
double, however, only can support a minimum value of about 5E-324, so if you get a number that small and try to divide it by 10, you'll get zero.
So your program loops about 324 times, and quits after it gets a small enough number.
Try printing out num inside the loop. You'll see it tries to divide by 10 (division like this can never end in 0) infinitely until it can no longer hold that small of a value and == 0 will return true.
Just because you set num to it's rounded value by using (int)num doesn't mean it'll behave like an int it'll still end up as a fractional when dividing, use an int if you want it to be:
Console.WriteLine("enter a number with change");
double num = double.Parse(Console.ReadLine());
int num2 = (int)num;
int c = 0;
Console.WriteLine(num2);
while (num2 != 0)
{
num2 /= 10;
c++;
}
Console.WriteLine(c);
This question already has answers here:
Division returns zero
(8 answers)
Closed 7 years ago.
I am starting to learn C# and here is a program that calculate percentage
using System;
namespace CheckDegree
{
class Program
{
static void Main(string[] args)
{
int totalmarks=0;
for (int i = 0; i < 5; i++ ) {
Console.WriteLine("Please enter a subject.");
string subject = Console.ReadLine();
Console.WriteLine("Enter the mark for {0}",subject);
int mark = Convert.ToInt32(Console.ReadLine());
totalmarks = totalmarks + mark;
}
double percentage = (totalmarks / 500) * 100;
if (percentage >= 60)
{
Console.WriteLine("You got {0} and has been awarded a first division", percentage );
}
else if (percentage >= 50 && percentage <= 59)
{
Console.WriteLine("You got {0} and has been awarded a second division", percentage);
}
else if (percentage < 40 )
{
Console.WriteLine("You got {0} and thus have failed!!!", percentage);
}
Console.ReadKey();
}
}
}
However percentage always output 0
double percentage = (totalmarks / 500) * 100;
Instead of dividing by 500, divide by 500.0. It's treating it as an integer divided by an integer, which will be 0.#####. Since it's an integer, the decimal gets dropped. Then, 0 times 100 is still 0.
Changing 500 to 500.0 will force the division to be a double, which will preserve your decimal value.
In my C# program I have a double obtained from some computation and its value is something like 0,13999 or 0,0079996 but this value has to be presented to a human so it's better displayed as 0,14 or 0,008 respectively.
So I need to round the value, but have no idea to which precision - I just need to "throw away those noise digits".
How could I do that in my code?
To clarify - I need to round the double values to a precision that is unknown at compile time - this needs to be determined at runtime. What would be a good heuristic to achieve this?
You seem to want to output a value which is not very different to the input value, so try increasing numbers of digits until a given error is achieved:
static double Round(double input, double errorDesired)
{
if (input == 0.0)
return 0.0;
for (int decimals = 0; decimals < 17; ++decimals)
{
var output = Math.Round(input, decimals);
var errorAchieved = Math.Abs((output - input) / input);
if (errorAchieved <= errorDesired)
return output;
}
return input;
}
}
static void Main(string[] args)
{
foreach (var input in new[] { 0.13999, 0.0079996, 0.12345 })
{
Console.WriteLine("{0} -> {1} (.1%)", input, Round(input, 0.001));
Console.WriteLine("{0} -> {1} (1%)", input, Round(input, 0.01));
Console.WriteLine("{0} -> {1} (10%)", input, Round(input, 0.1));
}
}
private double PrettyRound(double inp)
{
string d = inp.ToString();
d = d.Remove(0,d.IndexOf(',') + 1);
int decRound = 1;
bool onStartZeroes = true;
for (int c = 1; c < d.Length; c++ )
{
if (!onStartZeroes && d[c] == d[c - 1])
break;
else
decRound++;
if (d[c] != '0')
onStartZeroes = false;
}
inp = Math.Round(inp, decRound);
return inp;
}
Test:
double d1 = 0.13999; //no zeroes
double d2 = 0.0079996; //zeroes
double d3 = 0.00700956; //zeroes within decimal
Response.Write(d1 + "<br/>" + d2 + "<br/>" + d3 + "<br/><br/>");
d1 = PrettyRound(d1);
d2 = PrettyRound(d2);
d3 = PrettyRound(d3);
Response.Write(d1 + "<br/>" + d2 + "<br/>" + d3 +"<br/><br/>");
Prints:
0,13999
0,0079996
0,00700956
0,14
0,008
0,007
Rounds your numbers as you wrote in your example..
I can think of a solution though it isn't very efficient...
My assumption is that you can tell when a number is in the "best" human readable format when extra digits make no difference to how it is rounded.
eg in the example of 0,13999 rounding it to various numbers of decimal places gives:
0
0.1
0.14
0.14
0.14
0.13999
I'd suggest that you could loop through and detect that stable patch and cut off there.
This method seems to do this:
public double CustomRound(double d)
{
double currentRound = 0;
int stability = 0;
int roundLevel = 0;
while (stability < 3)
{
roundLevel++;
double current = Math.Round(d, roundLevel);
if (current == currentRound)
{
stability++;
}
else
{
stability = 1;
currentRound=current;
}
}
return Math.Round(d, roundLevel);
}
This code might be cleanable but it does the job and is a sufficient proof of concept. :)
I should emphasise that that initial assumption (that no change when rounding) is the criteria we are looking at which means that something like 0.3333333333 will not get rounded at all. With the examples given I'm unable to say if this is correct or not but I assume if this is a double issues that the problem is with the very slight variations from the "right" value and the value as a double.
Heres what I tried:
public decimal myRounding(decimal number)
{
double log10 = Math.Log10((double) number);
int precision = (int)(log10 >= 0 ? 0 : Math.Abs(log10)) + (number < 0.01m ? 1 : 2);
return Math.Round(number, precision);
}
test:
Console.WriteLine(myRounding(0.0000019999m)); //0.000002
Console.WriteLine(myRounding(0.0003019999m)); //0.0003
Console.WriteLine(myRounding(2.56777777m)); //2.57
Console.WriteLine(myRounding(0.13999m)); //0.14
Console.WriteLine(myRounding(0.0079996m)); //0.008
You can do it without converting to string. This is what I created fast:
private static double RoundDecimal(double number)
{
double temp2 = number;
int temp, counter = 0;
do
{
temp2 = 10 * temp2;
temp = (int)temp2;
counter++;
} while (temp < 1);
return Math.Round(number, counter < 2 ? 2 : counter);
}
or
private static double RoundDecimal(double number)
{
int counter = 0;
if (number > 0) {
counter = Math.Abs((int) Math.Log10(number)) + 1;
return Math.Round(arv, counter < 2 ? 2 : counter);
}
After giving it another thought I did the following and looks like it does what I want so far.
I iterate over the number of digits and compare Round( value, number ) and Round( value, number + 1 ). Once they are equal (not == of course - I compare the difference against a small number) then number is the number of digits I'm looking for.
Double.ToString() can take a string format as an argument. This will display as many characters as you require, rounding to the decimal place. E.G:
double Value = 1054.32179;
MessageBox.Show(Value.ToString("0.000"));
Will display "1054.322".
Source
Generic formats (i.e, pre-generated)
How to generate custom formats
You can use no of digits with Math.Round Function
Double doubleValue = 4.052102;
Math.Round(doubleValue, 2);
This will return 4.05 as your required answer.
This is tested code, can u explain me how i am wrong. So i need to change.
I have a file that stores exam scores for a class of students. I am trying to write a program that successfully opens the file, reads in the exam scores, finds the average score, the highest score, and the lowest score, and prints these out. The average score should be printed with 2 digits after the decimal point.
This is what I have so far:
static void Main()
{
string myData = "";
int temp = 0;
int max = 0;
int min = 0;
double average = 0;
StreamReader fileReader = new StreamReader("data.txt");
do
{
myData = fileReader.ReadLine();
if (myData != null)
{
max = int.Parse(myData);
temp = int.Parse(myData);
if (temp > max)
temp = max;
}
} while (myData != null);
fileReader.Close();
Console.ReadLine();
}//End Main()
I don't exactly know how to proceed. How do I read in a new line and assign it to temp? I don't think I'm doing it right.
Here is one way that will make your teacher sad :P
static void Main(string[] args)
{
var file = new StreamReader("scores.txt");
var split = file.ReadToEnd().Split(Environment.NewLine.ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
IEnumerable<int> ints = split.Select(x => int.Parse(x));
Console.WriteLine("Total Scores:" + ints.Count());
Console.WriteLine("Max:" + ints.Max());
Console.WriteLine("Min:" + ints.Min());
Console.WriteLine("Average:" + ints.Average().ToString("0.00"));
Console.ReadLine();
}
While this is technically correct, this is counter-productive to understanding algorithms (however basic they may be), and I suggest you look at the other answers. But this demonstrates how versatile the .NET framework is.
<3 LINQ
The first error I found is that max will always be exactly equal to temp because you assign them the same value, so this if will never be true:
max = int.Parse(myData);
temp = int.Parse(myData);
if (temp > max)
Not bad. Here's some pseudocode:
File.ReadAllLines to get an array of strings (call this lines)
Foreach line in lines
Parse the double (? is each line guaranteed to be an int?)
if max < parsed, max = parsed
if min > parsed, min = parsed
sum += parsed
Print out min, max, (sum / lines.count).ToString("000.00")
Assumes the file looks like:
25
12
33.5
100
75
...
Since this is homework, I'll just give you some clues.
Right now, you're setting "max" each time through the loop. Try only setting temp, and see what happens. You might want to consider defaulting max (when you create it) to a very small number instead of 0.
Also, you'll need to do something similar for "min", but default it to a very large number.
int max = int.MinValue;
int min = int.MaxValue;
In order to get the average, you'll need to have a sum and a count, and keep track of those. Then, at the end, use a double to compute the average, and print. To get 2 decimal places, you can use average.ToString("N") - the "N" format does a nicely formatted number with 2 decimal places by default.
How about you separate out the processes? Do the reading and fill a list of integers with the contents of the file. Then perform the processing for min / max and average later on.
Isolating issues help you focus on them. I like to call this noise reduction. As a contractor I get to work on a lot of messy code and one of the reasons they are hard to understand is that too much is going on at the same time. If you simplify whats going on the code almost writes itself. This is also called Separation of Concerns. This is a very important programming principle.
After you've isolated the issues and got the code working you can then try to put it all together again so the process is more efficient (if you do it inline with the file reading then you will only hold one line in memory at a time).
For starters, you need to get rid of the line "max = int.Parse(myData)". Otherwise, you'll keep overwriting max with the current value.
I take it that this is the general gist of your assignment. You should be leery of copying this code.
double count = 0.0;
double min = double.MaxValue;
double max = double.MinValue;
double total = 0.0;
using(StreamReader sr = new StreamReader(#"c:\data.txt"))
{
while (!sr.EndOfStream)
{
String line = sr.ReadLine();
double value = double.Parse(line.Trim());
if (value < min) min = value;
if (value > max) max = value;
total += value;
count++;
}
}
Console.WriteLine("Min: {0}", min);
Console.WriteLine("Max: {0}", max);
Console.WriteLine("Avg: {0}", (total / count).ToString("0.00"));
Console.ReadLine();
static void Main(string[] args)
{
const string filename = #"data.txt";
bool first = true;
int min=0, max=0, total=0;
var lines = File.ReadAllLines(filename);
foreach (var line in lines)
{
var score = int.Parse(line.Trim());
if (first)
{
min = max = total = score;
first = false;
continue;
}
if (score < min)
min = score;
if (score > max)
max = score;
total += score;
}
if (first)
{
Console.WriteLine("no input");
return;
}
var average = (double)total/lines.Length;
Console.WriteLine(string.Format("Min: {0}, Max: {1}, Average: {2:F2}", min, max, average));
}
Hey thanks to everybody who helped out and offered suggestions, here is my final code implementation (without using arrays)
using System;
using System.IO;
class Program
{
static void Main()
{
string line = "";
int value = 0;
int max = 0;
int min = 100;
int total = 0;
double count = 0.0;
double average = 0;
StreamReader fileReader = new StreamReader(#"data.txt");
do
{
line = fileReader.ReadLine();
if (line != null)
{
value = int.Parse(line);
if (value > max)
max = value;
if (value < min)
min = value;
total += value;
count++;
}
} while (line != null);
average = total / count;
Console.WriteLine("Max: {0}", max);
Console.WriteLine("Min: {0}", min);
Console.WriteLine("Avg: {0:f2}", average);
Console.ReadLine();
}//End Main()
}//End class Program