Round decimal value to nearest 5 or 0 amount - c#

I have a web application that will apply a percentage markup to a product, but the percentage will be specified by a user. For example, the user can indicate they want to mark up a product 5%, 9%, 23%, etc. My problem is, the product price will change as well, and in doing so, end up giving ugly values ($1462.72)
As a result, my users are hoping that we can round the value to the nearest 5\0 value. So if my marked up product price is $1462.72, it would round up to $1465. $1798.02 on the other hand would round up to an even $1800.
Using VB\C#, how can I go about rounding these values?
Thanks!

To round to an arbitrary modulus you can create a function like:
public decimal Round(decimal source, decimal modulus)
{
return (Math.Round(source / modulus) * modulus);
}
and use it in this way:
decimal rounded = Round(1798.02m , 5.0m); // yields 1800.0
decimal rounded = Round(1462.72m , 5.0m); // yields 1465.0
decimal rounded = Round(2481.23m , 5.0m); // yields 2480.0
Note that Math.Round by default rounds midpoint values to the closest even number (e.g. 1.5 and 2.5 would both "round" to 2. In your case, the effect is that any numbers that are exactly between a 0 and 5 number (i.e. 2.5, 7.5) would be rounded to the closest 10:
decimal rounded = Round(1697.50m , 5.0m); // yields 1700.0
decimal rounded = Round(1702.50m , 5.0m); // yields 1700.0
If you want to always round UP on the midpoint just specify that in Round:
return (Math.Round(source / modulus, MidpointRounding.AwayFromZero) * modulus);

You can use the modulus operator to calculate the adjustment needed.
decimal price = 1798.02;
decimal adjustment = price % 5.0;
if(adjustment != 0) //so we don't round up already round numbers
{
price = (price - adjustment) + 5;
}
This will bring it up to the next multiple of 5.

Related

Get Integer and Fractional part of a decimal number

Given X.Y, I want to get X and Y.
For instance, given 123.456 I want to get 123 and 456 (NOT 0.456).
I can do the following:
decimal num = 123.456M;
decimal integer = Math.Truncate(num);
decimal fractional = num - Math.Truncate(num);
// integer = 123
// fractional = 0.456 but I want 456
REF
As above-mentioned, using this method I will get 0.456, while I need 456. Sure I can do the following:
int fractionalNums = (int)((num - Math.Truncate(num)) * 1000);
// fracionalNums = 456
Additionally, this method requires knowing how many fractional numbers a given decimal number has so that you can multiply to that number (e.g., 123.456 has three, 123.4567 has four, 123.456789 has six, 123.1234567890123456789 has nineteen).
Few points to consider:
This operation will be executed millions of times; hence, performance is critical (maybe a bit-wise-based solution would do better);
Precision is critical, and no rounding is acceptable.
NOTE 1
For performance reasons, I am NOT interested in string manipulation-based approaches.
NOTE 2
The numbers in my question are of decimal type, hence methods that work for only decimal types and fail on float or double (due to floating point precision) are acceptable.
NOTE 3
Two sides of decimal (i.e., integer and fractional parts) can be considered two integers. Hence, 123.000456 is not an expected input; and even if it is given, it is acceptable to split it to 123 and 456 (because both sides are to be considered integers).
BitConverter.GetBytes(decimal.GetBits(num)[3])[2]; - number of digits after comma
long[] tens = new long[] {1, 10, 100, 1000, ...};
decimal num = 123.456M;
int iPart = (int)num;
decimal dPart = num - iPart;
int count = BitConverter.GetBytes(decimal.GetBits(num)[3])[2];
long pow = tens[count];
Console.WriteLine(iPart);
Console.WriteLine((long)(dPart * pow));
Decimal has a 96 bit mantissa, so a long is not good enough to get every possible value.
Define all (positive) powers of 10 defined for Decimal:
decimal mults[] = {1M, 1e1M, 1e2M, 1e3M, <insert rest here>, 1e27M, 1e28M};
Then, inside the loop you need to get the scale (the power of 10 by which the "mantissa" is divided to get the nominal value of the decimal):
int[] bits = Decimal.GetBits(n);
int scale = (bits[3] >> 16) & 31; // 567.1234 represented as 5671234 x 10^-4
decimal intPart = (int)n; // 567.1234 --> 567
decimal decPart = (n - intPart) * mults[scale]; // 567.1234 --> 0.1234 --> 1234
The easiest way is probably to convert the number to string.
Then take the substring after the decimal point, and convert it back to int.

Decimal not rounding properly

I have a decimal called 'sum' and its value is 5824088.999120m, but when I try to round it to 3 decimals I get 5824088.998m instead of 5824088.999m. It decrements instead of leaving 5824088.999m.
Why is it? Here's my code:
List<decimal> quantityList = QuantityList();
List<decimal> priceList = PriceList();
decimal destination = 5824088.999M;
decimal sum = 0M;
bool lifesaver = false;
for (int x = 0; x < priceList.Count; x++)
{
sum = 0M;
for (int i = 0; i < 3500; i++)
{
priceList[x] += 0.001M;
sum = 0M;
for (int y = 0; y < priceList.Count; y++)
{
decimal multipleProduct = priceList[y] * quantityList[y];
sum = sum + multipleProduct;
Console.WriteLine(priceList[y] + " " + quantityList[y]);
sum = Math.Round(sum, 3);
Console.WriteLine("Sum: " + sum);
Console.ReadKey();
Console.WriteLine();
}
if (sum == destination)
{
Console.WriteLine("The new number is " + priceList[x] + " and it is the {0} element!", x);
lifesaver = true;
break;
}
else if (sum > destination)
{
Console.WriteLine("Limit exceeded!");
}
if (i == 3499)
{
priceList[x] -= 3.500M;
}
if (lifesaver == true)
{
break;
}
}//Second for loop
if (lifesaver == true)
{
break;
}
}//Main for loop
The lists are in another method.
It seems that you have round up error accumulation and thus the total is wrong:
for (int y = 0; y < priceList.Count; y++) {
...
sum = Math.Round(sum, 3); // <- this accumulates round up errors
...
}
imagine that the priceList contains
priceList = new List<Decimal>() {
1.0004M, 1.0004M, 1.0004M};
while quantityList are all 1's; the sum will be
1.000M, 2.000M, 3.000M
while the actual total is
Math.Round(1.0004M + 1.0004M + 1.0004M, 3)
3.001M.
Possible remedy is not rounding up prematurely:
for (int y = 0; y < priceList.Count; y++) {
...
//DONE: comment out this: no premature rounding (within the loop)
// sum = Math.Round(sum, 3);
//DONE: but format out when printing out
Console.WriteLine("Sum: {0:F3}", sum);
...
}
// round up (if you want) after the loop
sum = Math.Round(sum, 3);
You should look into MidpointRounding (https://msdn.microsoft.com/en-us/library/system.midpointrounding(v=vs.110).aspx) and add this to your Math.Round function. Like this: sum = Math.Round(sum, 3, MidpointRounding.ToEven);, the other option you have is MidpointRounding.AwayFromZero, which might be better for you.
It is because of the Math.Round method.
Here is the MSDN documentation.
This method is equivalent to calling the Round(Decimal, Int32, MidpointRounding) method with a mode argument of MidpointRounding.ToEven.When d is exactly halfway between two rounded values, the result is the rounded value that has an even digit in the far right decimal position. For example, when rounded to two decimals, the value 2.345 becomes 2.34 and the value 2.355 becomes 2.36. This process is known as rounding toward even, or banker's rounding. It minimizes rounding errors that result from consistently rounding a midpoint value in a single direction.
You should try the Math.Round(decimal, int32, System.MidpointRounding), which can be found here
Try calling it with the MidPointRounding enum AwayFromZero value.
Example:
decimal d = 5824088.999120M;
decimal rounded = Math.Round(d, 3, System.MidpointRounding.AwayFromZero);
Console.WriteLine(rounded.ToString());
Logs 5824088.999
From remarks:
The decimals parameter specifies the number of significant decimal places in the return value and ranges from 0 to 28. If decimals is zero, an integer is returned.
In a midpoint value, the value after the least significant digit in the result is precisely half way between two numbers. For example, 3.47500 is a midpoint value if it is to be rounded two decimal places, and 7.500 is a midpoint value if it is to be rounded to an integer. In these cases, the nearest value can't be easily identified without a rounding convention, which is specified by the mode argument. The Round(Decimal, Int32, MidpointRounding) method supports two rounding conventions for handling midpoint values.
Rounding away from zero.
Midpoint values are rounded to the next number away from zero. For example, 3.75 rounds to 3.8, 3.85 rounds to 3.9, -3.75 rounds to -3.8, and -3.85 rounds to -3.9. This form of rounding is represented by the MidpointRounding.AwayFromZero enumeration member.
Rounding away from zero is the most widely known form of rounding.
Rounding to even, or banker's rounding
Midpoint values are rounded to the nearest even number. For example, both 3.75 and 3.85 round to 3.8, and both -3.75 and -3.85 round to -3.8. This form of rounding is represented by the MidpointRounding.ToEven enumeration member.
Rounding to nearest is the standard form of rounding used in financial and statistical operations. It conforms to IEEE Standard 754, section 4. When used in multiple rounding operations, it reduces the rounding error that is caused by consistently rounding midpoint values in a single direction. In some cases, this rounding error can be significant.
Check this .NET fiddle also. If you run it, you will see precisely your expected value 5824088.999

when trying to calculate discount price return 0 which is wrong c# Asp.net

When I'm trying to calculate a discount price, it returns 0 which is wrong.
The total is 100 and the percentage discount is 10%. I am sure about this formula, but it's keep returning 0.
My c# code :
protected void TxtDiscountPER_TextChanged(object sender, EventArgs e)
{
TxtDiscountAMT.Text = Convert.ToString(((Convert.ToInt16(TxtDiscountPER.Text)) / 100) * (Convert.ToInt16(TxtTotalAmt.Text)));
}
Here:
(Convert.ToInt16(TxtDiscountPER.Text)) / 100
you divide an Int16 (10) by an Int32 (100) using integer division, yielding 0. To quote from MSDN:
When you divide two integers, the result is always an integer. For example, the result of 7 / 3 is 2. To determine the remainder of 7 / 3, use the remainder operator (%). To obtain a quotient as a rational number or fraction, give the dividend or divisor type float or type double.
I suggest that you use the decimal data type instead of float or double, since decimal is better suited for monetary values:
(Convert.ToDecimal(TxtDiscountPER.Text)) / 100
The division will return a decimal value. You are converting the division to Int16. So it will always return an INT value which is 0

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);

How do you round a number to two decimal places in C#?

I want to do this using the Math.Round function
Here's some examples:
decimal a = 1.994444M;
Math.Round(a, 2); //returns 1.99
decimal b = 1.995555M;
Math.Round(b, 2); //returns 2.00
You might also want to look at bankers rounding / round-to-even with the following overload:
Math.Round(a, 2, MidpointRounding.ToEven);
There's more information on it here.
Try this:
twoDec = Math.Round(val, 2)
If you'd like a string
> (1.7289).ToString("#.##")
"1.73"
Or a decimal
> Math.Round((Decimal)x, 2)
1.73m
But remember! Rounding is not distributive, ie. round(x*y) != round(x) * round(y). So don't do any rounding until the very end of a calculation, else you'll lose accuracy.
Personally I never round anything. Keep it as resolute as possible, since rounding is a bit of a red herring in CS anyway. But you do want to format data for your users, and to that end, I find that string.Format("{0:0.00}", number) is a good approach.
Wikipedia has a nice page on rounding in general.
All .NET (managed) languages can use any of the common language run time's (the CLR) rounding mechanisms. For example, the Math.Round() (as mentioned above) method allows the developer to specify the type of rounding (Round-to-even or Away-from-zero). The Convert.ToInt32() method and its variations use round-to-even. The Ceiling() and Floor() methods are related.
You can round with custom numeric formatting as well.
Note that Decimal.Round() uses a different method than Math.Round();
Here is a useful post on the banker's rounding algorithm.
See one of Raymond's humorous posts here about rounding...
// convert upto two decimal places
String.Format("{0:0.00}", 140.6767554); // "140.67"
String.Format("{0:0.00}", 140.1); // "140.10"
String.Format("{0:0.00}", 140); // "140.00"
Double d = 140.6767554;
Double dc = Math.Round((Double)d, 2); // 140.67
decimal d = 140.6767554M;
decimal dc = Math.Round(d, 2); // 140.67
=========
// just two decimal places
String.Format("{0:0.##}", 123.4567); // "123.46"
String.Format("{0:0.##}", 123.4); // "123.4"
String.Format("{0:0.##}", 123.0); // "123"
can also combine "0" with "#".
String.Format("{0:0.0#}", 123.4567) // "123.46"
String.Format("{0:0.0#}", 123.4) // "123.4"
String.Format("{0:0.0#}", 123.0) // "123.0"
If you want to round a number, you can obtain different results depending on: how you use the Math.Round() function (if for a round-up or round-down), you're working with doubles and/or floats numbers, and you apply the midpoint rounding. Especially, when using with operations inside of it or the variable to round comes from an operation. Let's say, you want to multiply these two numbers: 0.75 * 0.95 = 0.7125. Right? Not in C#
Let's see what happens if you want to round to the 3rd decimal:
double result = 0.75d * 0.95d; // result = 0.71249999999999991
double result = 0.75f * 0.95f; // result = 0.71249997615814209
result = Math.Round(result, 3, MidpointRounding.ToEven); // result = 0.712. Ok
result = Math.Round(result, 3, MidpointRounding.AwayFromZero); // result = 0.712. Should be 0.713
As you see, the first Round() is correct if you want to round down the midpoint. But the second Round() it's wrong if you want to round up.
This applies to negative numbers:
double result = -0.75 * 0.95; //result = -0.71249999999999991
result = Math.Round(result, 3, MidpointRounding.ToEven); // result = -0.712. Ok
result = Math.Round(result, 3, MidpointRounding.AwayFromZero); // result = -0.712. Should be -0.713
So, IMHO, you should create your own wrap function for Math.Round() that fit your requirements. I created a function in which, the parameter 'roundUp=true' means to round to next greater number. That is: 0.7125 rounds to 0.713 and -0.7125 rounds to -0.712 (because -0.712 > -0.713). This is the function I created and works for any number of decimals:
double Redondea(double value, int precision, bool roundUp = true)
{
if ((decimal)value == 0.0m)
return 0.0;
double corrector = 1 / Math.Pow(10, precision + 2);
if ((decimal)value < 0.0m)
{
if (roundUp)
return Math.Round(value, precision, MidpointRounding.ToEven);
else
return Math.Round(value - corrector, precision, MidpointRounding.AwayFromZero);
}
else
{
if (roundUp)
return Math.Round(value + corrector, precision, MidpointRounding.AwayFromZero);
else
return Math.Round(value, precision, MidpointRounding.ToEven);
}
}
The variable 'corrector' is for fixing the inaccuracy of operating with floating or double numbers.
This is for rounding to 2 decimal places in C#:
label8.Text = valor_cuota .ToString("N2") ;
In VB.NET:
Imports System.Math
round(label8.text,2)
I know its an old question but please note for the following differences between Math round and String format round:
decimal d1 = (decimal)1.125;
Math.Round(d1, 2).Dump(); // returns 1.12
d1.ToString("#.##").Dump(); // returns "1.13"
decimal d2 = (decimal)1.1251;
Math.Round(d2, 2).Dump(); // returns 1.13
d2.ToString("#.##").Dump(); // returns "1.13"
Had a weird situation where I had a decimal variable, when serializing 55.50 it always sets default value mathematically as 55.5. But whereas, our client system is seriously expecting 55.50 for some reason and they definitely expected decimal. Thats when I had write the below helper, which always converts any decimal value padded to 2 digits with zeros instead of sending a string.
public static class DecimalExtensions
{
public static decimal WithTwoDecimalPoints(this decimal val)
{
return decimal.Parse(val.ToString("0.00"));
}
}
Usage should be
var sampleDecimalValueV1 = 2.5m;
Console.WriteLine(sampleDecimalValueV1.WithTwoDecimalPoints());
decimal sampleDecimalValueV1 = 2;
Console.WriteLine(sampleDecimalValueV1.WithTwoDecimalPoints());
Output:
2.50
2.00
One thing you may want to check is the Rounding Mechanism of Math.Round:
http://msdn.microsoft.com/en-us/library/system.midpointrounding.aspx
Other than that, I recommend the Math.Round(inputNumer, numberOfPlaces) approach over the *100/100 one because it's cleaner.
You should be able to specify the number of digits you want to round to using Math.Round(YourNumber, 2)
You can read more here.
Math.Floor(123456.646 * 100) / 100
Would return 123456.64
string a = "10.65678";
decimal d = Math.Round(Convert.ToDouble(a.ToString()),2)
public double RoundDown(double number, int decimalPlaces)
{
return Math.Floor(number * Math.Pow(10, decimalPlaces)) / Math.Pow(10, decimalPlaces);
}

Categories

Resources