How to round currency (decimals) to Millions? (C#) - c#

I have decimal currency amounts (from SQl2k* tables as 32,8) and I would like to round them to Millions with 2 decimal places.
The system will be displaying 1000s of rows with multiple amounts in them, and I need to summarise the totals for various categories into a "quick view".
e.g.
123,456,789.12345678
goes to
123.46
6,655,443,332.2110099
goes to
6,655.44
etc
I know there are issues with rounding and decimal/floating point math:
45,454,454.454545
goes to
45.46 OR 45.45 ?
so also any advice on what's best to use would also be very much appreciated.
Two more examples (of what I'd EXPECT...not what might actually be mathematically correct!)
555444444.444444 => 555.44
555444444.444445 => 555.45
I would expect the final "5" of the second example to cascade up to the decimal place?

decimal d = Math.Round(inValue/1000000m, 2);

double v = Math.Round(45454454.454545, 2);
Will return 45454454.45
So solution would be:
double factor = 1000000;
double v1 = Math.Round(123456789.12345678 / factor, 2); //// 123.46
double v2 = Math.Round(6655443332.2110099 / factor, 2); //// to 6,655.44

IMHO You should use Bankers Rounding instead of Your 'cascade' rounding

Related

Code for calculating amount of currency thinks it’s quirky. (Unintended Decimals keep appearing)

I’m just starting out learning C# through CodeAcademy. I’m supposed to write a program to calculate the least amount of “coins” of different values that are needed to reach a specified amount. It all went fine while following the instructions, but at the end of the exercise you are encouraged to write some more code to make the program work with decimal inputs (instead of just whole numbers).
I essentially copied the same code used for the complete amounts with slight modifications (multiplying the initial amount by 100) so that it could still run and give the required results. However, the last value (bronze cents) keeps giving me numbers with decimals for some reason. I thought about using Math.Floor() but after a few trials I realized it isn’t always an excess. Can anyone provide some help? Are there any inherent limits to the Math.Floor() command that I should have been aware of? Did I just do a big dumb?
tl;dr: noob coder here, wondering why code not do what i want it to do
using System;
namespace MoneyMaker
{
class MainClass
{
public static void Main(string[] args)
{
// This code is meant to divide an (user given) amount of money into coins of different values.
// First we ask for an input.
Console.WriteLine("Welcome to Money Maker!.00");
Console.WriteLine("Please enter the amount you want to divide in Coins:");
string strAmount = Console.ReadLine();
Console.WriteLine($"${strAmount} Canopy is equal to:");
double wholeAmount = Convert.ToDouble(strAmount);
// These are the values of each coin.
// The cents are multiplied by 100 for the purposes of using then in the code.
// The bronze coins are listed, but unused, since their value is essentially 1.
double gold = 10;
double silver = 5;
//double bronze = 1;
double smolGold = .1 * 100;
double smolSilver = .05 * 100;
//double smolBronze = .01 * 100;
// These lines calculate the integral values (gold, silver and bronze coins).
double douAmount = Math.Floor(wholeAmount);
double goldCoins = Math.Floor(douAmount / gold);
double silAmount = douAmount % gold;
double silverCoins = Math.Floor(silAmount / silver);
double bronzeCoins = silAmount % silver;
// These lines calculate the decimal values (gold, silver and bronze cents).
// They start by multiplying the cents by 100, rendering the rest of the lines the same as in the integral values.
double smolAmount = 100 * (wholeAmount - douAmount);
double goldCents = Math.Floor(smolAmount / smolGold);
double littleSilver = smolAmount % smolGold;
double silverCents = Math.Floor(littleSilver / smolSilver);
//ERROR: There seems to be an issue with the bronze cents, returning a value with decimals.
double bronzeCents = littleSilver % smolSilver;
// Finally, the output string with the results:
Console.WriteLine($"\n Gold Coins: {goldCoins} \n Silver Coins: {silverCoins} \n Bronze Coins: {bronzeCoins} \n Gold Cents: {goldCents} \n Silver Cents: {silverCents} \n Bronze Cents: {bronzeCents}");
}
}
}
Never use doubles for currency. Use decimal.
Use double when representing a physical quantity, like length, mass, speed, and so on, where insignificant representation errors do not matter. Use decimal to represent money, where every penny or fraction of a penny matters.
The difference is: double represents numbers as a fraction where the denominator is a power of two, like "5/32nds". decimal represents numbers as a fraction where the denominator is a power of ten, like "3/100ths". The former will tend to accumulate representation errors for fractions like "3/100ths", but the latter will not.
If your tutorial is suggesting that you use double arithmetic for computations involving money, get a better tutorial.
All that said, I suspect you may misunderstand what the % operator is for if you are using it on non-integer quantities. It is primarily for use on integer quantities, so if you're using it on decimals or doubles, something strange is probably going on.

Equation - Math.Round and double

I'm working on an equation for conversion rate. My current problem is I'm not sure if I'm getting the correct results.
In the Linq query when creating the GC_ConversionRateModel, I'm dividing TotalOrders with Visitors to get the conversion. In order to get a fluent result in Google Charts, I have to use 4 decimals, multiply the result with 100 and finally display the result with 2 decimals. (Not decimal data type).
Is this a good way to do this?
OrdersList.ForEach(o =>
{
TotalConversions.Add((from v in VisitorsList
where v.Date == o.Date
select new GC_ConversionRateModel(o.Date, (double)o.TotalOrders / (double)v.Visitors)).FirstOrDefault());
});
foreach (var conv in TotalConversions)
{
conv.ConversionRate = Math.Round(conv.ConversionRate, 4); // -- Round to 4 decimals
conv.ConversionRate = conv.ConversionRate * 100; //-- Multiply with 100
conv.ConversionRate = Math.Round(conv.ConversionRate, 2, MidpointRounding.ToEven); //-- Round to 2 decimals
}
return TotalConversions;
Thx
The second rounding line of your code never does anything useful because of the first rounding line: once you rounded to four decimal places, the number is going to look like this:
##.####00000
When you multiply by 100, the decimal dot will move two places to the right, so the number is going to look like this:
####.##00000
Note that the number has exactly two decimal digits, so the call of
conv.ConversionRate = Math.Round(conv.ConversionRate, 2, MidpointRounding.ToEven);
has no effect.
You can safely replace the three lines of conversion with one:
conv.ConversionRate = Math.Round(100*conv.ConversionRate, 2, MidpointRounding.ToEven);

Properly round financial data

I decided to re-create my question:
decimal dTotal = 0m;
foreach (DictionaryEntry item in _totals)
{
if (!string.IsNullOrEmpty(item.Value.ToString()))
{
dTotal += Convert.ToDecimal(item.Value);
}
}
Console.WriteLine(dTotal / 3600m);
Console.WriteLine(decimal.Round(dTotal / 3600m, 2));
Console.WriteLine(decimal.Divide(dTotal, 3600m));
The above code returns:
579.99722222222222222222222222
580.00
579.99722222222222222222222222
So, that is where my issues are coming from, I really need it to just display the 579.99; but any round, be it decimal.Round or Math.Round still return 580; even the string formats for {0:F} return 580.00.
How can i properly do this?
New answer (to new question)
Okay, so you've got a value of 579.99722222222222222222222222 - and you're asking that to be rounded to two decimal places. Isn't 580.00 the natural answer? It's closer to the original value than 579.99 is. It sounds like you essentially want flooring behaviour, but with a given number of digits. For that, you can use:
var floored = Math.Floor(original * 100) / 100;
In this case, you can do both in one step:
var hours = Math.Floor(dTotal / 36) / 100;
... which is equivalent to
var hours = Math.Floor((dTotal / 3600) * 100) / 100;
Original answer (to original question)
Sounds like you've probably got payTotal in an inappropriate form to start with:
using System;
class Test
{
static void Main()
{
decimal pay = 2087975.7m;
decimal time = pay / 3600;
Console.WriteLine(time); // Prints 579.99325
}
}
This is the problem:
var payTotal = 2087975.7;
That's assigning payTotal to a double variable. The value you've actually got is 2087975.69999999995343387126922607421875, which isn't what you wanted. Any time you find yourself casting from double to decimal or vice versa, you should be worried: chances are you've used the wrong type somewhere. Currency values should absolutely be stored in decimal rather than double (and there are various other Stack Overflow questions talking about when to use which).
See my two articles on floating point for more info:
Binary floating point in .NET
Decimal floating point in .NET
(Once you've got correct results, formatting them is a different matter of course, but that shouldn't be too bad...)

How to convert decimal to string value for dollars and cents separated in C#?

I need to display decimal money value as string, where dollars and cents are separate with text in between.
123.45 => "123 Lt 45 ct"
I came up with the following solution:
(value*100).ToString("#0 Lt 00 ct");
However, this solution has two drawbacks:
Upon showing this solution to a fellow programmer, it appears to be unintuitive and requires some explaining.
Cents are allways displayed as two digits. (Not real problem for me, as currently this is how I need it to be displayed.)
Is there any alternative elegant and simple solution?
This is a fairly simple operation. It should be done in a way, that your fellow programmers understand instantly. Your solution is quite clever, but cleverness is not needed here. =)
Use something verbose like
double value = 123.45;
int dollars = (int)value;
int cents = (int)((value - dollars) * 100);
String result = String.Format("{0:#0} Lt {1:00} ct", dollars, cents);
I had some errors with accepted answer above (it would drop my result one penny)
Here is my correction
double val = 125.79;
double roundedVal = Math.Round(val, 2);
double dollars = Math.Floor(roundedVal);
double cents = Math.Round((roundedVal - dollars), 2) * 100;
This might be a bit over the top:
decimal value = 123.45M;
int precision = (Decimal.GetBits(value)[3] & 0x00FF0000) >> 16;
decimal integral = Math.Truncate(value);
decimal fraction = Math.Truncate((decimal)Math.Pow(10, precision) * (value - integral));
Console.WriteLine(string.Format("{0} Lt {1} ct", integral, fraction));
The format of the decimal binary representation is documented here.

Comparing double values in C#

I've a double variable called x.
In the code, x gets assigned a value of 0.1 and I check it in an 'if' statement comparing x and 0.1
if (x==0.1)
{
----
}
Unfortunately it does not enter the if statement
Should I use Double or double?
What's the reason behind this? Can you suggest a solution for this?
It's a standard problem due to how the computer stores floating point values. Search here for "floating point problem" and you'll find tons of information.
In short – a float/double can't store 0.1 precisely. It will always be a little off.
You can try using the decimal type which stores numbers in decimal notation. Thus 0.1 will be representable precisely.
You wanted to know the reason:
Float/double are stored as binary fractions, not decimal fractions. To illustrate:
12.34 in decimal notation (what we use) means
1 * 101 + 2 * 100 + 3 * 10-1 + 4 * 10-2
The computer stores floating point numbers in the same way, except it uses base 2: 10.01 means
1 * 21 + 0 * 20 + 0 * 2-1 + 1 * 2-2
Now, you probably know that there are some numbers that cannot be represented fully with our decimal notation. For example, 1/3 in decimal notation is 0.3333333…. The same thing happens in binary notation, except that the numbers that cannot be represented precisely are different. Among them is the number 1/10. In binary notation that is 0.000110011001100….
Since the binary notation cannot store it precisely, it is stored in a rounded-off way. Hence your problem.
double and Double are the same (double is an alias for Double) and can be used interchangeably.
The problem with comparing a double with another value is that doubles are approximate values, not exact values. So when you set x to 0.1 it may in reality be stored as 0.100000001 or something like that.
Instead of checking for equality, you should check that the difference is less than a defined minimum difference (tolerance). Something like:
if (Math.Abs(x - 0.1) < 0.0000001)
{
...
}
You need a combination of Math.Abs on X-Y and a value to compare with.
You can use following Extension method approach
public static class DoubleExtensions
{
const double _3 = 0.001;
const double _4 = 0.0001;
const double _5 = 0.00001;
const double _6 = 0.000001;
const double _7 = 0.0000001;
public static bool Equals3DigitPrecision(this double left, double right)
{
return Math.Abs(left - right) < _3;
}
public static bool Equals4DigitPrecision(this double left, double right)
{
return Math.Abs(left - right) < _4;
}
...
Since you rarely call methods on double except ToString I believe its pretty safe extension.
Then you can compare x and y like
if(x.Equals4DigitPrecision(y))
Comparing floating point number can't always be done precisely because of rounding. To compare
(x == .1)
the computer really compares
(x - .1) vs 0
Result of sybtraction can not always be represeted precisely because of how floating point number are represented on the machine. Therefore you get some nonzero value and the condition evaluates to false.
To overcome this compare
Math.Abs(x- .1) vs some very small threshold ( like 1E-9)
From the documentation:
Precision in Comparisons
The Equals method should be used with caution, because two apparently equivalent values can be unequal due to the differing precision of the two values. The following example reports that the Double value .3333 and the Double returned by dividing 1 by 3 are unequal.
...
Rather than comparing for equality, one recommended technique involves defining an acceptable margin of difference between two values (such as .01% of one of the values). If the absolute value of the difference between the two values is less than or equal to that margin, the difference is likely to be due to differences in precision and, therefore, the values are likely to be equal. The following example uses this technique to compare .33333 and 1/3, the two Double values that the previous code example found to be unequal.
So if you really need a double, you should use the techique described on the documentation.
If you can, change it to a decimal. It' will be slower, but you won't have this type of problem.
Use decimal. It doesn't have this "problem".
Exact comparison of floating point values is know to not always work due to the rounding and internal representation issue.
Try imprecise comparison:
if (x >= 0.099 && x <= 0.101)
{
}
The other alternative is to use the decimal data type.
double (lowercase) is just an alias for System.Double, so they are identical.
For the reason, see Binary floating point and .NET.
In short: a double is not an exact type and a minute difference between "x" and "0.1" will throw it off.
Double (called float in some languages) is fraut with problems due to rounding issues, it's good only if you need approximate values.
The Decimal data type does what you want.
For reference decimal and Decimal are the same in .NET C#, as are the double and Double types, they both refer to the same type (decimal and double are very different though, as you've seen).
Beware that the Decimal data type has some costs associated with it, so use it with caution if you're looking at loops etc.
Official MS help, especially interested "Precision in Comparisons" part in context of the question.
https://learn.microsoft.com/en-us/dotnet/api/system.double.equals
// Initialize two doubles with apparently identical values
double double1 = .333333;
double double2 = (double) 1/3;
// Define the tolerance for variation in their values
double difference = Math.Abs(double1 * .00001);
// Compare the values
// The output to the console indicates that the two values are equal
if (Math.Abs(double1 - double2) <= difference)
Console.WriteLine("double1 and double2 are equal.");
else
Console.WriteLine("double1 and double2 are unequal.");
1) Should i use Double or double???
Double and double is the same thing. double is just a C# keyword working as alias for the class System.Double
The most common thing is to use the aliases! The same for string (System.String), int(System.Int32)
Also see Built-In Types Table (C# Reference)
Taking a tip from the Java code base, try using .CompareTo and test for the zero comparison. This assumes the .CompareTo function takes in to account floating point equality in an accurate manner. For instance,
System.Math.PI.CompareTo(System.Math.PI) == 0
This predicate should return true.
// number of digits to be compared
public int n = 12
// n+1 because b/a tends to 1 with n leading digits
public double MyEpsilon { get; } = Math.Pow(10, -(n+1));
public bool IsEqual(double a, double b)
{
// Avoiding division by zero
if (Math.Abs(a)<= double.Epsilon || Math.Abs(b) <= double.Epsilon)
return Math.Abs(a - b) <= double.Epsilon;
// Comparison
return Math.Abs(1.0 - a / b) <= MyEpsilon;
}
Explanation
The main comparison function done using division a/b which should go toward 1. But why division? it simply puts one number as reference defines the second one. For example
a = 0.00000012345
b = 0.00000012346
a/b = 0.999919002
b/a = 1.000081004
(a/b)-1 = 8.099789405475458e-5‬
1-(b/a) = 8.100445524503848e-5‬
or
a=12345*10^8
b=12346*10^8
a/b = 0.999919002
b/a = 1.000081004
(a/b)-1 = 8.099789405475458e-5‬
1-(b/a) = 8.100445524503848e-5‬
by division we get rid of trailing or leading zeros (or relatively small numbers) that pollute our judgement of number precision. In the example, the comparison is of order 10^-5, and we have 4 number accuracy, because of that in the beginning code I wrote comparison with 10^(n+1) where n is number accuracy.
Adding onto Valentin Kuzub's answer above:
we could use a single method that supports providing nth precision number:
public static bool EqualsNthDigitPrecision(this double value, double compareTo, int precisionPoint) =>
Math.Abs(value - compareTo) < Math.Pow(10, -Math.Abs(precisionPoint));
Note: This method is built for simplicity without added bulk and not with performance in mind.
As a general rule:
Double representation is good enough in most cases but can miserably fail in some situations. Use decimal values if you need complete precision (as in financial applications).
Most problems with doubles doesn't come from direct comparison, it use to be a result of the accumulation of several math operations which exponentially disturb the value due to rounding and fractional errors (especially with multiplications and divisions).
Check your logic, if the code is:
x = 0.1
if (x == 0.1)
it should not fail, it's to simple to fail, if X value is calculated by more complex means or operations it's quite possible the ToString method used by the debugger is using an smart rounding, maybe you can do the same (if that's too risky go back to using decimal):
if (x.ToString() == "0.1")
Floating point number representations are notoriously inaccurate because of the way floats are stored internally. E.g. x may actually be 0.0999999999 or 0.100000001 and your condition will fail. If you want to determine if floats are equal you need to specify whether they're equal to within a certain tolerance.
I.e.:
if(Math.Abs(x - 0.1) < tol) {
// Do something
}
My extensions method for double comparison:
public static bool IsEqual(this double value1, double value2, int precision = 2)
{
var dif = Math.Abs(Math.Round(value1, precision) - Math.Round(value2, precision));
while (precision > 0)
{
dif *= 10;
precision--;
}
return dif < 1;
}
To compare floating point, double or float types, use the specific method of CSharp:
if (double1.CompareTo(double2) > 0)
{
// double1 is greater than double2
}
if (double1.CompareTo(double2) < 0)
{
// double1 is less than double2
}
if (double1.CompareTo(double2) == 0)
{
// double1 equals double2
}
https://learn.microsoft.com/en-us/dotnet/api/system.double.compareto?view=netcore-3.1

Categories

Resources