2

Is there a method for Math calculations which eliminates division rounding errors entirely?

I am trying to calculate the average of a sensor output on the fly but after several millions of iterations my average is drifting away due to rounding error. enter image description here

Currently I am storing my values in memory which works fine but at the cost of memory/performance.

EG

int number1 = 10;
int number2 = 3;
Console.WriteLine((number1/number2));

result = 3 (3.333~!)

float number1 = 10
float number2 = 3
Console.Writeline((number1/number2))

result = 3.33333325 (3.3~)

Example from: https://learn.microsoft.com/en-us/dotnet/api/system.decimal?view=netcore-3.1

decimal dividend = Decimal.One;
decimal divisor = 3;
Console.WriteLine(dividend/divisor * divisor);

result = 0.9999999999999999999999999999 instead of 1(!!!)

This is an immense issue for my application as I am continuously applying division calculations to sensor Outputs and my values drift off slowly due to that fact. Windows Calculator seems to be able to work around that.

Is there any available solution or do I have to create my own framework?


edit: a possible solution might be to implement fractions as of ⅓. Otherwise a different approach to the problem might be required.

julian bechtold
  • 1,875
  • 2
  • 19
  • 49
  • 5
    It sounds like you want a library supporting rational arithmetic. A quick search finds https://github.com/tompazourek/Rationals as one such library. I don't think there's anything within .NET itself. – Jon Skeet Jun 15 '20 at 07:55
  • 1
    Have you considered multiplying _before_ dividing? – mjwills Jun 15 '20 at 07:57
  • 1
    Do you understand **why** the first code sample results in 3? – mjwills Jun 15 '20 at 07:57
  • Read the Remarks section of [Decimal.Round](https://learn.microsoft.com/en-us/dotnet/api/system.decimal.round) and [Is floating point math broken?](https://stackoverflow.com/q/588004/7444103). Your code can be declared as `IEEE Standard 754, section 4` compliant (unless you don't want it *compliant*, that is. In this case, you need to work out what values you need to return in your specific use case). – Jimi Jun 15 '20 at 08:09
  • @JonSkeet yes this looks a lot like what I'm searching for. – julian bechtold Jun 15 '20 at 08:11
  • @mjwills I have considered Multiplying first but came to the comclusion that you come to the same issue with 10/3 for example. the first code sample results in 3 because int cannot represent enough decimnal positions. However, the other types merely increase the presition by closer approximation which is not what I want. I am searching for a way to get precise calculations rather than aproximations.The end result might be aproximated but not the calculations in between. – julian bechtold Jun 15 '20 at 08:15
  • 3
    10m * 3 / 3 will definitely work is the point I was making. If that isn't sufficient, check out the link that @JonSkeet provided. – mjwills Jun 15 '20 at 08:29
  • 1
    `the first code sample results in 3 because int cannot represent enough decimnal positions.` Yes - as per https://stackoverflow.com/questions/10851273/why-does-integer-division-in-c-sharp-return-an-integer-and-not-a-float . – mjwills Jun 15 '20 at 08:30

2 Answers2

2

There is actually a way in arithmetics to go around precision problem, without fractions. It can be very hard to implement though, if your formula is dynamic (not hardcoded). Otherwise, you can just rearrange your operations to be more precise for specific domain of numbers, for example:

(x + 1) - x 
  • with big floats it will become somewhat 0
  • with small floats it will become somewhat 1 (small floats are more precise than big one)

So rearanging it like this, will give correct result in both:

(x - x) + 1

I know example is trvial, but for specific operations you should choose specific rearangement, for example knowing that floats closer to zero are MUCH more precise, you can just work with them only. In my example rearangement is such that I want to minimize impact of variable, so I tie them closer to each other, to destroy their bloating to big, more inprecise floats. For example, I will win this out if I had something like this:

x^1.05 + 1 - x^1.01

The trivial dynamic approach is generally to sort operations in ascending order - from lower floats operations, to bigger floats. Variables x,y,z,etc can be big or low, so here is a problem of sorting - you sort each time you pass those variables inside your formula, and it will give you best precision. Or you hardcode different permutations of rearrangement for different inputs.

Here is article: https://books.google.ru/books?id=KJORYTHOxbEC&pg=PA390&lpg=PA390&dq=rearrange+math+operations+for+precision&source=bl&ots=y8E8fjdrYy&sig=ACfU3U1vfkonygDnLJhSCK3qh0C2kaXK3w&hl=en&sa=X&ved=2ahUKEwiv6v2Xs4PqAhXGzaQKHTTUDlwQ6AEwAHoECAgQAQ

eocron
  • 6,885
  • 1
  • 21
  • 50
0

In decimal you must have such numeric problems. If you want to do some calculations with better fraction, you should use any fraction library, use fractions to keep values in memory and only show result as decimal point number.

Leszek Mazur
  • 2,443
  • 1
  • 14
  • 28
  • I do not think that more fractions can provide a proper solution for me. I might try to use fractions in the way of ⅓ and then only apply math in the end or I have to approach my problem in a different way. – julian bechtold Jun 15 '20 at 08:08
  • 2
    Only fractions guarantee solve numeric problems with divisions when you do operations one by one. Second solution is to keep all math operations in memory and solving it in numeric proper way, but it is much harder to implement. – Leszek Mazur Jun 15 '20 at 08:13
  • That is exactly the Issue I want to overcome. I do currently store all the data in memory which leads to memory bottleneck and performance decrease. I figured that I can achieve insane performance when calculating the values on the fly but at the cost of heavily drifting values after several millions of operations. – julian bechtold Jun 15 '20 at 08:30