C# midpoint round down option for decimal - c#

Im working with an external payment system which uses a round down at exact midpoint, but round up if its anything more than that. I want to replicate the same in my application to match the value.
For example with two decimal point rounding, 150.415 is rounded to 150.41 and 150.4151 is rounded to 150.42. I am not entirely sure what this rounding mechanism is called.
To replicate the same behaviour in C#, I tried using Math.Round(amount, 2, MidpointRounding.AwayFromZero) and Math.Round(amount, 2, MidpointRounding.ToEven), but both rounds off the above number to 150.42
Trying to see what are my options here, including writing a custom function to do a similar rounding?

I think this is the logic that you need:
public static decimal DigitalRiverRounding(decimal value)
{
if (value < 0)
return Math.Floor(value * 100 + 0.5m) / 100m;
return Math.Ceiling(value * 100 - 0.5m) / 100m;
}
If you are certain no negative numbers are involved, you can of course remove the first two lines. I assumed for negative numbers the desired output is mirrored (-150.415 => -150.41, so they compensate appropriately).
Explanation assuming rounding with no decimals: as Ceiling converts 1.01 - 2.00 to 2, by doing -0.5 you are translating that into a logic that does 0.51 - 1.50 to 1, therefore 1.51 - 2.50 to 2, which is what you need.
In case you need to use this everywhere in your app, you may want to use an extension method instead, which needs a separate static helper class:
public static decimal ToDigitalRiverRounding(this decimal value)
{
if (value < 0)
return Math.Floor(value * 100 + 0.5m) / 100m;
return Math.Ceiling(value * 100 - 0.5m) / 100m;
}

#include <iostream>
#include <cmath>
using namespace std;
double roundN(double val, int len)
{
double result;
result = val * pow(10.0, len);
result = static_cast<double>(static_cast<int>(result + 0.5));
result = result * pow(10.0, -len);
return result;
}
int main() {
double a;
cout<<"enter number"<<endl;
cin>>a;
a=roundN(a,2);
std::cout << a;
return 0;
}

Related

Round to 25, 50, 75, 100

I'm not a Math person so I'm having a hard time to come up with a calculation to round the decimals to 25, 50, 75 and 100. And this will not be the typical round off because the decimals will not be decreased but only increased.
Example:
if 11.12, round to 11.25
if 11.34, round to 11.50
if 11.52, round to 11.75
if 11.76, round to 12.00
Here's my starting method:
public float RoundNearestCents(String price)
{
float srp;
return srp;
}
public float RoundNearestCents(double d)
{
return (double)(Math.Ceiling(d * 4)) / 4;
}
I suggest using types without floating point.
decimal RoundNearestCents(decimal price) {
// no problems with floating point as all calculations are exact
return Math.Floor((price * 100 + 24) / 25) * 25 / 100;
}
-- Why is your price string?
-- Because it's coming from a textbox.
I assume your textbox should support limiting your input to decimal numbers with at most 2 decimal places. So its value will be decimal already. However I don't know what is your application type. If you still want to accept string then consider using decimal.TryParse method to convert it to decimal.
My code may not be the best out there, but it will work.
In your function create a float and an int like so.
public float RoundNearestCents(String price)
{
float srp = float.Parse(price);
int srp1 = Int32.Parse(price);
if((srp-srp1)>=0.5)
srp1++;
else
return srp1;
return srp1;
}
The int would truncate out the decimal part, which is like flooring the price.
I would use something like this:
float RoundNearestCents(float price)
{
price*=(100/25.0); // now fractions are going away
if (price-floor(price)>=0.5) price++; // round up if fraction above 0.5
return floor(price)*(25.0/100.0); // cut of the fraction and restore original range
}
This is one way:
public decimal RoundNearestCents(decimal price)
{
decimal srp = price * 100;
decimal m = srp % 25;
srp = srp - m + (m > 0 ? 25 : 0);
return srp / 100;
}

Keep rounding upto specified digit if non zero

I want to round Up decimal values upto two points. But for any number which is less than 0.01, I want to return 0.01.
RoundUp(0.146,2) should return 0.15
RoundUp(0.0003,2) should return 0.01
In C#, I am currently using Math.Round, with MidpointRounding.AwayFromZero parameter, but for
Math.Round(0.0003, 2, MidpointRounding.AwayFromZero);
it returns 0.
Is there any in built method in Math namespace, which I can use to get desired behavior?
Currently I am using this method
private double GetRoundUpValue(double price, int roundUpto)
{
Debug.Assert(roundUpto == 2 || roundUpto == 3);
var val = Math.Round(price, roundUpto, MidpointRounding.AwayFromZero);
Double minValue = roundUpto == 2 ? 0.01 : 0.001;
return val < minValue ? minValue : val;
}
But for any number which is less than 0.01, I want to return 0.01.
Then why not keep it simple and just use something like this:
Math.Max(Math.Round(0.0003, 2, MidpointRounding.AwayFromZero), 0.01);
Or if you need something more general, to round to n decimal places, use something like this:
private double GetRoundUpValue(double price, int places)
{
var minValue = Math.Pow(0.1, places);
return Math.Max(Math.Round(price, places, MidpointRounding.AwayFromZero), minValue);
}
Also note, that 'rounding away from zero' is not the same as 'rounding up' (for that, you can look at Math.Ceiling). So I'd recommend either changing the name of this method or it's body to be more clear about what's actually going on inside of it.
For example:
private double GetRoundUpValue(double price, int places)
{
var scale = Math.Pow(10, places);
return Math.Ceiling(price * scale) / scale;
}

double and rounding is very hard, but this is crazy

double a = 135.24; // a is set to 135.24000000000001 actually
double b = Math.Round(a, 0); // set to 135.0
double c = Math.Round(a, 1); // set to 135.19999999999999
double d = Math.Round(a, 2); // set to 135.24000000000001
double e = Math.Round(a, 3); // set to 135.24000000000001
double f = Math.Round(a, 4); // set to 135.24000000000001
double g = Math.Round(a, 5); // set to 135.24000000000001
double h = Math.Round(a, 10); // set to 135.24000000000001
double i = Math.Round(a, 14); // set to 135.24000000000001
double j = Math.Round(a, 2
, MidpointRounding.AwayFromZero ); // set to 135.24000000000001
double k = Math.Round(a, 2
, MidpointRounding.ToEven ); // set to 135.24000000000001
Sooooo, this means that 135.24 cannot be represented with a double, right?
Yes, 135.24 cannot be represented by double since double uses binary exponential notation.
That is: 135.24 can be represented exponentially in base of 2 as 1.0565625 * 128 = ( 1 + 1/32 + 1/64 + 1/128 + 1/1024 + ... ) * (2**7).
The representation cannot be done exactly, because 13524 does not divide by 5. Let's look:
135.24 = 13524/(10**2)
representation is finite <=> exist whole x and n satisfying 135.24 = x/(2**n)
135.24 = x/(2**n)
13524 / (10**2) = x / (2**n)
13524 * (2**n) = (10**2) * x
13524 * (2**n) = 2*2*5*5 * x
there is no "5" on the left side, so it cannot be done
(known as the Fundamental Theorem of Arithmetic)
In general, finite binary representation is exact only if there is sufficient number of "fives" in prime factorization of the decimal number.
Now the fun part:
double delta = 0.5;
while( 1 + delta > 1 )
delta /= 2;
Console.WriteLine( delta );
Precision of double is different near 1, different near 0, and different for some big numbers. Some binary representation examples on Wikipedia: Double precision floating point format
But the most important thing is that internal processor floating-point stack may have much better precision than 8 bytes (double). If number does not have to be transferred to RAM and stripped down to 8 bytes we can get a really nice precision.
Testing something like this on different processors (AMD, Intel), languages (C, C++, C#, Java) or compiler optimization levels can give results can be around 1e-16, 1e-20, or even 1e-320
Take a look at CIL / assembler / jasmin code to see exactly what is going on (eg: for C++ g++ -S test.cpp creates test.s file with assembler code in it)
That is generally a problem with floating point numbers. If you need an exact representation of numbers (e.g. for billing, ...) then you should use Decimal.
Try following piece of code and you will see that you do not have the output 0, 0.1, 0.2, ... 1.0.
for(double i = 0; i <= 1.0; i += 0.001)
{
Console.WriteLine(i);
}
Try using decimal instead. Floating points are not very precise (thus some numbers can't be represented) :)
Yes, it cannot. This is why there's another nonintegra datatype called decimal. It takes different amount of memory and has different min/max numerical ranges than double, and is NOT bit-convertible*) to double, but in turn it can held numbers precisely without any distortions.
*) That is, you cannot i.e. copy it as bytes and push to C++ code. However, you can still cast it to double and back. Just mind that the cast will NOT be precise, as double cannot hold some numbers that decimal can, and vice versa
You can see the definition.The Round function defined as-
public static double Round(double value, int digits, MidpointRounding mode)
{
if (digits < 0 || digits > 15)
throw new ArgumentOutOfRangeException("digits", Environment.GetResourceString("ArgumentOutOfRange_RoundingDigits"));
if (mode >= MidpointRounding.ToEven && mode <= MidpointRounding.AwayFromZero)
return Math.InternalRound(value, digits, mode);
throw new ArgumentException(Environment.GetResourceString("Argument_InvalidEnumValue", (object) mode, (object) "MidpointRounding"), "mode");
}
private static unsafe double InternalRound(double value, int digits, MidpointRounding mode)
{
if (Math.Abs(value) < Math.doubleRoundLimit)
{
double num1 = Math.roundPower10Double[digits];
value *= num1;
if (mode == MidpointRounding.AwayFromZero)
{
double num2 = Math.SplitFractionDouble(&value);
if (Math.Abs(num2) >= 0.5)
value += (double) Math.Sign(num2);
}
else
value = Math.Round(value);
value /= num1;
}
return value;
}

C# implementation of T-SQL round function

I'm trying to reproduce in C# the behavior of the T-SQL Round function when a truncate function is used.
This is the SQL behavior I'm trying to reproduce in C#:
SELECT ROUND(150.757,2,0) -- 150.76
SELECT ROUND(150.757,2,1) -- 150.75
SELECT ROUND(150.747,2,0) -- 150.75
SELECT ROUND(150.747,2,1) -- 150.74
In System.Math there are two methods that I tried to use.
The first one, Math.Truncate truncates to the whole part only, so it won't help me.
The other method is Math.Round.
This method has 2 interesting overloads.
Math.Round(decimal,int)
Math.Round(decimal,int,System.MidpointRounding)
The MidpointRounding enumeration options are:
System.MidpointRounding.AwayFromZero
// When a number is halfway between two others,
// it is rounded toward the nearest number that is away from zero.
System.MidpointRounding.ToEven
// When a number is halfway between two others,
// it is rounded toward the nearest even number.
Executing the two overloads of Math.Round with the same data as SQL I've got the following result:
Math.Round(150.757, 2, MidpointRounding.AwayFromZero) // 150.76
Math.Round(150.757, 2, MidpointRounding.ToEven) // 150.76
Math.Round(150.747, 2, MidpointRounding.AwayFromZero) // 150.75
Math.Round(150.747, 2, MidpointRounding.ToEven) // 150.75
Given that none of the MidpointRounding solve my problem, what is the best way to reproducte the T-SQL function in C#?
Update:
After implementing Paul's answer I noticed one extra odd behavior from T-SQL ROUND function:
SELECT ROUND(150.747,-2,1) // 100
SELECT ROUND(150.747,-2) // 200
I edited Paul's answer to include support for this edge case.
I imagine somebody will come up with a better way, but this is certainly a possible way!
using System;
static class Program
{
static void Main(string[] args)
{
Console.WriteLine(150.757.TruncateWithDecimalPlaces(2));
Console.WriteLine(150.747.TruncateWithDecimalPlaces(2));
Console.Read();
}
public static double TruncateWithDecimalPlaces(this double input, int decimalPlaces)
{
double factor = Math.Pow(10, decimalPlaces);
return Math.Truncate(input*factor)/factor;
}
}
Output:
150.75
150.74
A more complete implementation would look something like this:
public static double Round(double input, int decimalPlaces, int roundType = 0)
{
double factor = Math.Pow(10, decimalPlaces);
if (roundType == 0)
{
if (decimalPlaces >= 0)
{
return Math.Round(input, decimalPlaces);
}
return Math.Round(input * factor) / factor;
}
return Math.Truncate(input * factor) / factor;
}
I've elaborated a bit on Paul's answer so that his method provides the same behavior as TSQL's round (btw I didn't make my version an extension method):
using System;
namespace TestTSql
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine(TSqlRound(150.757, 2, false)); // 150.76
Console.WriteLine(TSqlRound(150.757, 2, true)); // 150.75
Console.WriteLine(TSqlRound(150.747, 2, false)); // 150.75
Console.WriteLine(TSqlRound(150.747, 2, true)); // 150.74
Console.ReadKey();
}
public static double TSqlRound(double input, int length, bool truncate = false)
{
if (truncate)
{
double factor = Math.Pow(10, length);
return Math.Truncate(input * factor) / factor;
}
else return Math.Round(input, length);
}
}
}

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