C# Correct Rounding Taking Into Account All the Digits - c#

I use decimal variables in c#
I want to get Math.round(0.004449m, 2) = 0.01
I mean start rounding from the last digit ie: 9 in this example. Since Math.round(0.004449m, 5) = 0.00445 treat 0.004449m as 0.0445 at the first place.
Keep rounding. Since Math.round(0.0445m , 4) = 0.045 treat 0.0445m as 0.045
Finally Math.round(0.004449m, 2) is found to be 0.01.
I know this approach is mathematically wrong but there is also a logic behind it so my company decided to round numbers like this.
Is there this kind of function in .net?

Related

How to round a float to one digit after the dot

Most probably, it's a duplicate, and I don't understand some basics, and thus deserve a downvote, but it just drives me crazy.
I'm using C# and .NET Core on Mac OS.
I have a float 9.74999 and I want to round it to one digit after the dot and get 9.8 (like it should work according to what I remember from primary school: 9.749 -> 9.75 -> 9.8 -> 10).
I tried this:
float rounded = (float)Math.Round(9.74999f, 1, MidpointRounding.AwayFromZero);
// rounded = 9.7
Why it's not 9.8? So, it just takes the digit of interest (7) and the following one (4) and doesn't care about the rest (999)?
And this construction actually gives me what I want:
float rounded = (float)Math.Round(
(float)Math.Round(9.74999f, 2, MidpointRounding.AwayFromZero),
1,
MidpointRounding.AwayFromZero
);
// rounded = 9.8
So, the only way I can get 9.8 out of 9.74999 is by performing two Round operations:
First Round with 2 decimals;
Second Round with the result of the first one and 1 decimal.
Is this a proper way to do it?
Update
I see now that at school we were given a concept of double (triple, etc) rounding (https://en.wikipedia.org/wiki/Rounding#Double_rounding), which accumulates error on each step, and that's not the rounding that is used by Math.Round, so that's why I am not getting what I expect to get (9.749 -> 9.75 -> 9.8).
But I do need to perform exactly such a "consequential" rounding, going from right to left, digit by digit. So, I guess, there is no standard function for that, and I need to implement it myself.
In Math, 9.74999 rounds to 9.7. The code is using math.
Edit with less snark: 9.75 and greater will around to 9.8. Anything less than 9.75 (and greater than or equal to 9.65) will round to 9.7. I think you're confusing yourself and thinking that the 999 will round the .74 to a .75, but that's not the case.

Exact double precision by correct rounding

Although my question sounds trivial, it really is NOT. Hope you can help me.
I want to implement interval arithmetic in my .NET (C#) project. This means that every number is defined by an lower bound and an upper bound. This is helpfull for problems like
1 / 3 = 0.333333333333333 (15 significant digits)
since you would then have
1 / 3 = [ 0.33333333333333 , 0.333333333333334 ] (14 significant digits each)
, so I now FOR SURE that the right answer lays between those two numbers. Without the interval representation I would already have a rounding error with me (i.e. 0.0000000000000003).
To achieve this I wrote my own Interval type that overloads all standard operators like +-*/, etc. To make this type work correctly I need to be able to round the result of 1 / 3 in two directions. Rounding the result down will give me the lower bound for my interval, rounding the result up will give me the upper bound for my interval.
.NET has the Math.Round(double,int) method which rounds the double to int decimal places. Looks great but it can't be forced to round up/down. Math.Round(1.0/3.0,14) would round down, but the also needed up-rounding to 0.33...34 can't be achieved like this.
But there are Math.Ceil and Math.Floor you might say! Okay, those methods round to the next lower or upper integer. So if I want to round to 14 decimal places I first need to reform my result:
1 / 3 = 0.333333333333333 -> *E14 -> 33333333333333.3
So now I can call Math.Ceil and Math.Floor and get both rounded results after reforming back
33333333333333 & 33333333333334 -> /E14 -> 0.33333333333333 & 0.33333333333334
Looks great, but: Let's say my number goes near the double.MaxValue. I can't just *E14 a value near double.MaxValue since this will give me an OverflowException. So this is no solution either.
And, to top all of these facts: All this fails even harder when trying to round 0.9999999999999999999999999 (more than 15 digits) since the internal representation is already rounded to 1 before I can even start trying to round down.
I could try to somehow parse a string containing the double but this won't help since (1/3 * 3).ToString() will already print 1 instead of 0.99...9.
Decimal does not work either since I don't want that deep precision, 14 digits are enough; but I still want that double range!
In C++, where several interval arithmetic implementations exist, this problem could be solved by telling the processor dynamically to swith its roundmode to for example "always down" or "always up". I couldn't find any way to do this in .NET.
So, do you have any ideas?
Thanks in advance!
Assume nextDown(x) is a function that returns the largest double that is less than x, and nextUp(x) is a function that returns the smallest double that is greater than x. See Get next smallest Double number for implementation ideas.
Where you would have rounded a lower bound result down, instead use the nextDown of the round-to-nearest result. Where you would have rounded an upper bound up, use the nextUp of the round-to-nearest result.
This method ensures the interval continues to contain the exact real number result. It introduces extra rounding error - in some cases the lower bound will be one ULP smaller than it should be, and/or the upper bound will be one ULP bigger. However, it is a minimal widening of the interval, much less widening than you would get working in decimal or by suppressing low significance bits.
This might be more like a long comment than a real answer.
This code returns an "interval" (I just use Tuple<,>, you can use your own Interval type) based on truncating the seven least significant bits:
static Tuple<double, double> GetMinMaxIntervalBasedOnBinaryNumbersThatAreRoundOnLastSevenBits(double number)
{
if (double.IsInfinity(number) || double.IsNaN(number))
return Tuple.Create(number, number); // maybe treat this case differently
var i = BitConverter.DoubleToInt64Bits(number);
const int numberOfBitsToClear = 7; // your seven, can change this value, must be below 52
const long precision = 1L << numberOfBitsToClear;
const long bitMask = ~(precision - 1L);
//truncate i
i &= bitMask;
return Tuple.Create(BitConverter.Int64BitsToDouble(i), BitConverter.Int64BitsToDouble(i + precision));
}
Disclaimer: I am not sure if this is useful for any purpose. In particular not sure it is useful for interval arithmetic.
With this code, GetMinMaxIntervalBasedOnBinaryNumbersThatAreRoundOnLastSevenBits(1.0 / 3.0) returns the tuple (0.333333333333329, 0.333333333333336).
This code, just like the code you ask for in your question, has the obvious "issue" that if the original value is close to (or even equal to) one of the "round" numbers we use, then the returned interval is "skewed", with the original number being close to one of the ends of the interval. For example, with input 42.0 (already round), you get out the tuple (42, 42.0000000000009).
One good thing about this code is I expect it to be extremely fast.

Value of a double variable not exact after multiplying with 100 [duplicate]

This question already has answers here:
Is floating point math broken?
(31 answers)
Closed 7 years ago.
If I execute the following expression in C#:
double i = 10*0.69;
i is: 6.8999999999999995. Why?
I understand numbers such as 1/3 can be hard to represent in binary as it has infinite recurring decimal places but this is not the case for 0.69. And 0.69 can easily be represented in binary, one binary number for 69 and another to denote the position of the decimal place.
How do I work around this? Use the decimal type?
Because you've misunderstood floating point arithmetic and how data is stored.
In fact, your code isn't actually performing any arithmetic at execution time in this particular case - the compiler will have done it, then saved a constant in the generated executable. However, it can't store an exact value of 6.9, because that value cannot be precisely represented in floating point point format, just like 1/3 can't be precisely stored in a finite decimal representation.
See if this article helps you.
why doesn't the framework work around this and hide this problem from me and give me the
right answer,0.69!!!
Stop behaving like a dilbert manager, and accept that computers, though cool and awesome, have limits. In your specific case, it doesn't just "hide" the problem, because you have specifically told it not to. The language (the computer) provides alternatives to the format, that you didn't choose. You chose double, which has certain advantages over decimal, and certain downsides. Now, knowing the answer, you're upset that the downsides don't magically disappear.
As a programmer, you are responsible for hiding this downside from managers, and there are many ways to do that. However, the makers of C# have a responsibility to make floating point work correctly, and correct floating point will occasionally result in incorrect math.
So will every other number storage method, as we do not have infinite bits. Our job as programmers is to work with limited resources to make cool things happen. They got you 90% of the way there, just get the torch home.
And 0.69 can easily be represented in
binary, one binary number for 69 and
another to denote the position of the
decimal place.
I think this is a common mistake - you're thinking of floating point numbers as if they are base-10 (i.e decimal - hence my emphasis).
So - you're thinking that there are two whole-number parts to this double: 69 and divide by 100 to get the decimal place to move - which could also be expressed as:
69 x 10 to the power of -2.
However floats store the 'position of the point' as base-2.
Your float actually gets stored as:
68999999999999995 x 2 to the power of some big negative number
This isn't as much of a problem once you're used to it - most people know and expect that 1/3 can't be expressed accurately as a decimal or percentage. It's just that the fractions that can't be expressed in base-2 are different.
but why doesn't the framework work around this and hide this problem from me and give me the right answer,0.69!!!
Because you told it to use binary floating point, and the solution is to use decimal floating point, so you are suggesting that the framework should disregard the type you specified and use decimal instead, which is very much slower because it is not directly implemented in hardware.
A more efficient solution is to not output the full value of the representation and explicitly specify the accuracy required by your output. If you format the output to two decimal places, you will see the result you expect. However if this is a financial application decimal is precisely what you should use - you've seen Superman III (and Office Space) haven't you ;)
Note that it is all a finite approximation of an infinite range, it is merely that decimal and double use a different set of approximations. The advantage of decimal is it produces the same approximations that you would if you were performing the calculation yourself. For example if you calculated 1/3, you would eventually stop writing 3's when it was 'good enough'.
For the same reason that 1 / 3 in a decimal systems comes out as 0.3333333333333333333333333333333333333333333 and not the exact fraction, which is infinitely long.
To work around it (e.g. to display on screen) try this:
double i = (double) Decimal.Multiply(10, (Decimal) 0.69);
Everyone seems to have answered your first question, but ignored the second part.

Rounding floats with `E` in C#

I've got 7,0975401565468943E+22
And Math.Round(x, 3) returns 7,0975401565468943E+22
Is it normal behavior and should I check if number contains E and if so just use something alike ToString("N2"); ?
code example:
float flo = float.Parse( " 7,0975401565468943E+22 " );
double flox = Math.Round(flo, 3);
The behavior you describe appears normal (though without a code example it is impossible to know for sure).
Your number has no significant digits in the fractional portion, as displayed. Note the "E+22", this means that you are dealing with a very large number. There are only 17 significant digits displayed, with another 5 digits not shown before you get to the decimal point. You can round to as many fractional digits as you want, you won't see any change in the number being displayed.

c# Subtract is not accurate even with decimals?

I'm learning TDD and, decided to create a Calculator class to start.
i did the basic first, and now I'm on the Square Root function.
I'm using this method to get the root http://www.math.com/school/subject1/lessons/S1U1L9DP.html
i tested it with few numbers, and I always get the accurate answers.
is pretty easy to understand.
Now I'm having a weird problem, because with some numbers, im getting the right answer, and with some, I don't.
I debugged the code, and found out that I'm not getting the right answer when I use subtract.
I'm using decimals to get the most accurate result.
when I do:
18 / 4.25
I am currently getting: 4.2352941176470588235294117647
when it should be: 4.2352941176470588235294117647059 (using windows calculator)
in the end of the road, this is the closest i get to the root of 18:
4.2426406871192851464050688705 ^ 2 = 18.000000000000000000000022892
my question is:
Can i get more accurate then this?
4.2352941176470588235294117647 contains 29 digits.
decimal is define to have 28-29 significant digits. You can't store a more accurate number in a decimal.
What field of engineering or science are you working in where the 30th and more digits are significant to the accuracy of the overall calculation?
(It would also, possibly, help if you'd shown some more actual code. The only code you've shown is 18 / 4.25, which can't be an actual expression in your code, since the second number is a double literal, and you can't assign the result of this expression to a decimal without a cast).
If you need arbitrary precision, then there isn't a standard "BigRational" type, but there is a BigInteger. You could use that to construct a BigRational type if you need that (storing numerator and denominator as two separate integers). One guess of why there isn't a standard type yet is that decisions on when to e.g. normalize such rationals may affect performance or equality comparisons.
Floating point calculations are not accurate. Decimals make the accuracy better, because they are 128-bit long, but they are still floating point numbers.
Comparing two floating point numbers is not done with ==, but rather:
static bool SameDecimal(decimal a, decimal b)
{
return Math.Abs(a-b) < 1e-10;
}
This method will allow you to compare two decimals (I assume 1e-10 is a small enough difference for you, it should be for everyday uses).

Categories

Resources