In my database, I have three point floating numbers columns and a process that multiplies the two first ones to get the third one value
item.ValorTotal = Math.Round(item.Qtde * item.ValorUnitario, 2, MidpointRounding.AwayFromZero);
but if my item.Qtde is like 0.03 and my item.ValorUnitario is 0.02, item.ValorTotal the result is 0.0006 and the variable receives zero because of the round, how do I can round to get 0.01 and continue using two numbers after decimal point?
In short I do like to round to the first possible number (0.01) when I receive a lower number like 0.006 or 0.0006
Instead of Math.Round() you can use Math.Ceiling().
The Math.Ceiling() method in C# is used to return the smallest integral value greater than or equal to the specified number.
So in your code example it will be something like:
item.ValorTotal = (Math.Ceiling((item.Qtde * item.ValorUnitario) * 100) / 100);
Output:
0,0006 => 0,01
0,0106 => 0,02
AwayFromZero doesn't mean that you always round upward. In real it works at most values like usual rounding. As far as I understood it only has an effect if you would round 0.005x.
Therefore write
item.ValorTotal = Math.Round(item.Qtde * item.ValorUnitario + 0.005, 2, MidpointRounding.AwayFromZero);
when you want to round upward and both values are positive.
Related
One of my unit tests failed today. I nailed it down to a rather peculiar decimal rounding issue.
var b = 196.5m;
var result1 = b / 12m; //16.375
var result2 = b * 1 / 12m; //16.375
var result3 = b * (1 / 12m); //16.374999999999999999999999993
What's happening there with result3?
I would very much prefer to use result3 code style (the actual code is much more complex than this obviously) but I want the result3 to be identical to result1 and result2.
-- UPDATE --
I have even rounded it:
Math.Round(196.5m * (1 / 12m), 2, MidpointRounding.AwayFromZero); //16.37
Math.Round(196.5m / 12m, 2, MidpointRounding.AwayFromZero); //16.38
This might be relevant, so I want to start by pointing out the Language Specification, section 4.1.7
If one of the operands of a binary operator is of type decimal, then the other operand must be of an integral type or of type decimal. If an integral type operand is present, it is converted to decimal before the operation is performed.
The result of an operation on values of type decimal is that which would result from calculating an exact result (preserving scale, as defined for each operator) and then rounding to fit the representation. Results are rounded to the nearest representable value, and, when a result is equally close to two representable values, to the value that has an even number in the least significant digit position (this is known as “banker’s rounding”). A zero result always has a sign of 0 and a scale of 0.
This tells you results3 and 4 in your test should give identical results. (Also note the "integral" word, implicit float/double conversion is not supported)
Now, in your case you've stumbled upon an equation with some nice simplification properties, in that 196.5 / 12 => 393 * (2/3) / 12 => 131 * 2 / 4. In your results3&4, you calculate the division by 3 first (1/12), which gives 0.0833..., something that can't be exactly represented in decimal. And then you scale 0.0833... up (your order of operations is to divide by 12 then multiple by b).
You can get the same result by first rounding a number that can't be represented in decimal, e.g., something with repeating digits, say 1/7m. For example, 917m * (1 / 7m) = 131.00000000000000000000000004 but note 917m/7m = 131.
You can mitigate this by preferring multiplications first (being careful of overflow). The other option is to round your results.
This is probably a dupe of something like Is C# Decimal Rounding Inconsistent?
or maybe
Rounding of decimal in c# seems wrong...
the problem lies in order of operations and precision of returned values
In your example the first does no multiplication, and the second does the multiplication first and then the division, the 3rd will first do the division, which will return 0,0833333333333333...
this will then be represented as 0,0833333333333333 which introduces an imprecision of 3,333...e-17*. this imprecision is then multiplied
When doing stuff like this, try to make sure to first do the multiplications and afterwards the divisions. Doing so will eliminate the risk of multiplying with imprecise fractions
-REMARK-* the exact number here is dependent on which type (decimal, float,...)is returned, which is in turn dependent on which types you are dividing, it likely is not 17
If you want to magnify this issue, try thinking this out in int
I have a problem to round a number to two decimal places.
I have the number 3106.4647771976413339683766317M.
The correct round to two decimal places is 3106.47, but using Math.Round(value, 2, MidpointRounding.AwayFromZero) the number is 3106.46.
The problem is the method look to third decimal place to round, but if it look to fourth decimal place will generate the correct number.
Someone has something like that?
Mathematically, the correct round to two decimal places is 3106.46.
What you probably want is a ceiling:
Math.Ceiling(3106.4647771976413339683766317M * 100) / 100
produces 3106.47. There is no version of Math.Ceiling accepting a number of decimal places, that's why there are multiplication and division.
In addition, note that there is a caveat in this expression:
Math.Round(value, 2, MidpointRounding.AwayFromZero)
Math.Round does not have a variant with three arguments, where the first one is a decimal. It works, because the value is implicitly converted to double. However, this is unwanted.
If you really want to round that way (more of a common mistake than actual rounding in the traditional sense) you can round up from the furthest right digit and move left one place at a time.
double numberToRound = 3106.4647771976413339683766317;
int placesToRoundTo = 2;
int smallestPlaceIndex = 24; // you would need to determine this value
for (int i = smallestPlaceIndex; i >= placesToRoundTo)
{
numberToRound = Math.Round(numberToRound, i, MidpointRounding.AwayFromZero);
}
There isn't a simple built-in method to do this because it isn't normal rounding.
I'm testing a system which gave 10% of discount in a product, this means 102.555 of discount, but the system only use 2 fractional digits, so it gave 102.55.
The problem is if I execute this:
Math.Round(102.555, 2, MidpointRounding.AwayFromZero)
The output is: 102.56
And if I execute this:
Math.Round(102.555, 2, MidpointRounding.ToEven)
The output is also 102.56.
I was using Math.Round method with all scenarios, until this came up.
What am I doing wrong?
Why is 102.555 returning 102.56 with MidpointRounding.AwayFromZero?
How Can I do something to return 102.55 from 102.555?
Why is 102.555 returning 102.556 with MidpointRounding.AwayFromZero?
I presume "102.556" should read "102.56" here.
Because MidpointRounding indicates what happens when you are rounding a number where the significant digit for rounding (i.e. the last disappearing digit) at radix 10 is 5. This is the case for your number 102.555, so when rounding to two decimal places, there are two options:
102.55
102.56
MidpointRounding.AwayFromZero picks the option that is further away from zero - in this case (as these are positive numbers) the greater one, namely 102.56.
Ok, I found the answer:
https://stackoverflow.com/a/13483693/375422
public double TruncateDown(double number, int decimalPlaces)
{
return Math.Floor(number * Math.Pow(10, decimalPlaces)) / Math.Pow(10, decimalPlaces);
}
public double TruncateUp(double number, int decimalPlaces)
{
return Math.Ceiling(number * Math.Pow(10, decimalPlaces)) / Math.Pow(10, decimalPlaces);
}
In my case, I want to round down.
While string truncation will work, you want the floor() function used this way:
double value = Math.Floor(Math.Int(sourceVal * 100)) / 100;
There are two types of rounding, the banker's rounding (i.e. to even), and everyday rounding (i.e. away from zero).
Simply put, away from zero rounding simply checks the number before the precision specified, and if it is 5 or greater, then it rounds up.
However, to even checks whether it will approach an even number when it rounds up, and if so, then it rounds up. However, it it approaches an odd number, then it won't round up.
Bear in mind, the default method Math.Round(x, y) uses to even implicitly.
To even gives a complimentary correct calculation. See here. 1.5 + 2.5 = 4. If you round each one and sum them up, you will still get 4, however, you will get calculation error if you do the same with away from zero.
var x = Math.Round(1.5, 0) + Math.Round(2.5, 0); // 4
var y = Math.Round(1.5, 0, MidpointRounding.AwayFromZero) + Math.Round(2.5, 0, MidpointRounding.AwayFromZero); // 5!
See here for more info: https://msdn.microsoft.com/en-us/library/system.midpointrounding(v=vs.110).aspx
Edit 1:
Following O. R. Mapper & sgmoore comments, I just realized that the point of bankers rounding is to have the odds of rounding up or down almost equally the same over the course of random numbers.
In away from zero rounding, you end up with 1-4 (four odds) rounding down, and 5-9 rounding up (5 odds).
However, in bankers rounding, 1-4 (four odds) will round down, and 6-9 will round up (4 odds), and then we have 5 that will either round up or down, and when applied to random numbers, the odds of rounding up is almost like the odds of rounding down. And that gives a better chance for more accurate calculation specially when summing up.
You can find out more here: Why does .NET use banker's rounding as default?
Edit 2:
If you want to truncate, you can simply use:
decimal x = 102.555M;
decimal truncated = Math.Truncate(x * 100) / 100; // 102.55;
There is a great system to round to the nearest integer adding half to the number.
For example if you want to round 3.8 to 4, you first add 0.5 (it will bring it to 4.3) and then cut the 0 (using mod or int).
In your sample, you need to add 0.005 and then cut the last digit.
It can be done with toFixed(2) to keep two digits after the dot.
Here you have some console output...
(102.555+.005).toFixed(2)
"102.56"
(102.555).toFixed(2)
"102.56"
toFixed rounds to the nearest number so in this case you don't need to add half but to substract it.
(102.555-.005).toFixed(2)
"102.55"
Here's my formula:
int a;
int b;
int c;
double multiplier;
amount = Convert.ToInt32(Math.Round(multiplier * a * (4 * b + c) / 100, 0));
When I get a value that would make it have a decimal of .5 or greater it's rounding down instead. How do I make it so that this equation will round off the final result (and I know how to display as a string)?
The default rounding behvaior for Math.Round is ToEven, which rounds numbers on the midpoint to the nearest even integer. it does not always round down, as you are claiming, it rounds down exactly half of the time, and round up exactly half of the time. If you wish the midpoint to always round away from zero then you need to specify MidpointRounding.AwayFromZero to Math.Round.
(In addition to the problem Servey pointed out...)
Be mindful of the mode for some rounding methods. In some cases, you need to use the "AwayFromZero" mode in order for decimals .5 and greater to round up, and less than .5 to round down.
See the documentation. The "Math.Round(Decimal, MidpointRounding) example" shows what I mean.
I'm stuck with a little issue here, say you have the following code:
int whole = 0;
double decimal = 88.00
whole = decimal / 1.5; //now the answer is 58.66
So here's the issue, explicitly casting a double to an int is easy enough. But if I do this now 'whole' is going to be set to 59. This - is not so good, I want it to be set to the last whole number (being 58).
How do you do this in C#?
To round doubles to integers, you have 4 basic math functions:
Math.Round() - Rounds to the nearest whole number (or user specified number of deciml places), and lets you choose to round middle points up or down.
Math.Floor() - Rounds to the first whole number toward negative infinity.
Math.Ceiling() - Rounds to the first whole number toward positive infinity.
Math.Truncate() - Rounds to the first whole number toward zero.
I think you want either Floor or Truncate. Both round down for positive numbers, but Truncate rounds -3.6 to -3, while Floor rounds it to -4.
Casting to int does the same as truncating, so you can use that if you prefer.
Math.Floor:
whole = (int)Math.Floor(decimal / 1.5);
If you cast double to int, the answer will NOT be 59 -- it will be 58. When casting double to int, the value will be rounded towards zero. So, this is sufficient:
int whole = 0;
double x = 88.00;
whole = (int)(x / 1.5); // whole will be 58
Use Math.Floor if you want to round to the last whole number, and Math.Ceiling if you want to round to the next.
If you need rounding, it's pretty common to use:
whole = (int)(decimal / 1.5 + 0.5);
Without the 0.5, you're truncating, not rounding.
If you have a rounding function in your math libraries, that's good too. Some of these will do the odd/even thing for rounding 0.5 to avoid a little bit of data skew.
This will convert the double value into int:
whole = (int)(decimal / 1.5);
Also, you can use Math.Floor(doubleValue).