C# implementation of T-SQL round function - c#

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

Related

C# midpoint round down option for decimal

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

How to make Random.Range() step by a certain amount?

this is a very simple question, but I couldn't find out how to solve it. Lets say I have this line:
Random.Range(2,4);
I want this to generate not only whole numbers but also halves. What I mean is the step should be 0.5. I want it to choose randomly from the following only:
2, 2.5, 3, 3.5
I don't want any other decimal numbers. Can somebody help?
This is a simple solution for your specific case.
var randomNumber = ((float)Random.Range(4, 8)) * 0.5f;
You can also extend the method to work with different values.
public float GetRandomFloat(int min, int max, float value = 0.5f)
{
int multipliedMin = (int) (max / value);
int multipliedMax = (int) (max / value);
return ((float) Random.Range(multipliedMin, multipliedMax)) * value;
}
System.Random class doesn't have the feature you need natively , you can make your own method or extension for it
method
// use only one instance of System.Random to avoid getting duplicates
static System.Random ra = new Random();
static double GenRnadNum(int min,int max,double step)
{
// calc the max-min and round down
int n = (int)((max - min) / step);
int r= ra.Next(0, n);
return min + r * step;
}
usage example dd.Add(GenRnadNum(5, 20, 0.7));
i see many people doing it by dividing the max and min by the step (max/step,min/step)
and then multiplying the result random by the step (randomRestul*step)
but this will generate wrong results for example min=2,max=4,step=0.3 can give you 2.4 with that algorithm (2/0.3=6.66-->7 ,random result=8, 8*0.3=2.4)

Modulo calculation with big numbers (C#)

today in class we learned that a big modulo calculation can be divided into multiple smaller modulo calculations.
The example was 7^5 mod 11 (=10) can be divided into (((5^2) mod11)^2 mod11)*7 mod11. That sounded understandable.
I built a method in C# and for the example and some other values it is working, but for most of the calculation results with other numbers (comparing to Wolfram Alpha) my code is wrong.
public static int moduloForBigNumbers(int baseValue, int exponent, int modulo)
{
int result;
int newBase = baseValue;
while (exponent > 1)
{
newBase = (int)Math.Pow(newBase, 2) % modulo;
exponent = exponent - 2;
}
if (exponent == 1)
{
result = (newBase * baseValue) % modulo;
return result;
}
return newBase;
}
For example when using 7, 560, 561 i get a wrong result. My first guesses were, that the numbers were too big, so i tried it with long. My 2nd guess is that something with the %-calculation is going wrong but i can't figure it out.
Thanks for any help.

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

Truncate number of digit of double value in C#

How can i truncate the leading digit of double value in C#,I have tried Math.Round(doublevalue,2) but not giving the require result. and i didn't find any other method in Math class.
For example i have value 12.123456789 and i only need 12.12.
EDIT: It's been pointed out that these approaches round the value instead of truncating. It's hard to genuinely truncate a double value because it's not really in the right base... but truncating a decimal value is more feasible.
You should use an appropriate format string, either custom or standard, e.g.
string x = d.ToString("0.00");
or
string x = d.ToString("F2");
It's worth being aware that a double value itself doesn't "know" how many decimal places it has. It's only when you convert it to a string that it really makes sense to do so. Using Math.Round will get the closest double value to x.xx00000 (if you see what I mean) but it almost certainly won't be the exact value x.xx00000 due to the way binary floating point types work.
If you need this for anything other than string formatting, you should consider using decimal instead. What does the value actually represent?
I have articles on binary floating point and decimal floating point in .NET which you may find useful.
What have you tried? It works as expected for me:
double original = 12.123456789;
double truncated = Math.Truncate(original * 100) / 100;
Console.WriteLine(truncated); // displays 12.12
double original = 12.123456789;
double truncated = Truncate(original, 2);
Console.WriteLine(truncated.ToString());
// or
// Console.WriteLine(truncated.ToString("0.00"));
// or
// Console.WriteLine(Truncate(original, 2).ToString("0.00"));
public static double Truncate(double value, int precision)
{
return Math.Truncate(value * Math.Pow(10, precision)) / Math.Pow(10, precision);
}
How about:
double num = 12.12890;
double truncatedNum = ((int)(num * 100))/100.00;
This could work (although not tested):
public double RoundDown(this double value, int digits)
{
int factor = Math.Pow(10,digits);
return Math.Truncate(value * factor) / factor;
}
Then you simply use it like this:
double rounded = number.RoundDown(2);
This code....
double x = 12.123456789;
Console.WriteLine(x);
x = Math.Round(x, 2);
Console.WriteLine(x);
Returns this....
12.123456789
12.12
What is your desired result that is different?
If you want to keep the value as a double, and just strip of any digits after the second decimal place and not actually round the number then you can simply subtract 0.005 from your number so that round will then work. For example.
double x = 98.7654321;
Console.WriteLine(x);
double y = Math.Round(x - 0.005, 2);
Console.WriteLine(y);
Produces this...
98.7654321
98.76
There are a lot of answers using Math.Truncate(double).
However, the approach using Math.Truncate(double) can lead to incorrect results.
For instance, it will return 5.01 truncating 5.02, because multiplying of double values doesn't work precisely and 5.02*100=501.99999999999994
If you really need this precision, consider, converting to Decimal before truncating.
public static double Truncate(double value, int precision)
{
decimal power = (decimal)Math.Pow(10, precision);
return (double)(Math.Truncate((decimal)value * power) / power);
}
Still, this approach is ~10 times slower.
I'm sure there's something more .netty out there but why not just:-
double truncVal = Math.Truncate(val * 100) / 100;
double remainder = val-truncVal;
If you are looking to have two points after the decimal without rounding the number, the following should work
string doubleString = doublevalue.ToString("0.0000"); //To ensure we have a sufficiently lengthed string to avoid index issues
Console.Writeline(doubleString
.Substring(0, (doubleString.IndexOf(".") +1) +2));
The second parameter of substring is the count, and IndexOf returns to zero-based index, so we have to add one to that before we add the 2 decimal values.
This answer is assuming that the value should NOT be rounded
For vb.net use this extension:
Imports System.Runtime.CompilerServices
Module DoubleExtensions
<Extension()>
Public Function Truncate(dValue As Double, digits As Integer)
Dim factor As Integer
factor = Math.Pow(10, digits)
Return Math.Truncate(dValue * factor) / factor
End Function
End Module
I use a little formatting class that I put together which can add gaps and all sorts.
Here is one of the methods that takes in a decimal and return different amounts of decimal places based on the decimal display setting in the app
public decimal DisplayDecimalFormatting(decimal input, bool valueIsWeightElseMoney)
{
string inputString = input.ToString();
if (valueIsWeightElseMoney)
{
int appDisplayDecimalCount = Program.SettingsGlobal.DisplayDecimalPlacesCount;
if (appDisplayDecimalCount == 3)//0.000
{
inputString = String.Format("{0:#,##0.##0}", input, displayCulture);
}
else if (appDisplayDecimalCount == 2)//0.00
{
inputString = String.Format("{0:#,##0.#0}", input, displayCulture);
}
else if (appDisplayDecimalCount == 1)//0.0
{
inputString = String.Format("{0:#,##0.0}", input, displayCulture);
}
else//appDisplayDecimalCount 0 //0
{
inputString = String.Format("{0:#,##0}", input, displayCulture);
}
}
else
{
inputString = String.Format("{0:#,##0.#0}", input, displayCulture);
}
//Check if worked and return if worked, else return 0
bool itWorked = false;
decimal returnDec = 0.00m;
itWorked = decimal.TryParse(inputString, out returnDec);
if (itWorked)
{
return returnDec;
}
else
{
return 0.00m;
}
}
object number = 12.123345534;
string.Format({"0:00"},number.ToString());

Categories

Resources