Domanda

When division results in an infinitely repeating number, the number obviously gets truncated to fit into the size of the decimal. So something like 1/3 becomes something like 0.3333333333333333333. If we then multiply that number by 3, we get something like 0.999999999999999999 rather than 1 as we would get if the true value of the fraction had been preserved.

This is a code sample of this from the MSDN article on decimal:

decimal dividend = Decimal.One;
decimal divisor = 3;
// The following displays 0.9999999999999999999999999999 to the console
Console.WriteLine(dividend/divisor * divisor); 

This causes an issue when the value 0.9999999999999999999 is compared with 1 for equality. Without the loss of precision they would be equal, but of course in this case the comparison would result in false.

How do people typically deal with this problem? Is there a more elegant solution other than defining some margin of error to every comparison?

È stato utile?

Soluzione

This is a very old and widely known numeric computation problem. You already said you are looking for a solution other than defining some margin of error to every comparison. An approach that comes to my mind is to build the mathematic expression tree in memory first and do the calculation last. Having this at hand we can do some simplifications using some known rules before doing the calculations. Rules such as:

  • Remove equal numbers in numerator and denominator of a fraction if they are not zero
  • Square root of a square of a number is the number itself
  • ...

Therefore instead of storing 1/3 in a decimal/double value which equals 0.33333... we can store an instance of Fraction(1, 3). Then we can define all other expressions like this to build an expression rather than doing the calculations. In the end we can first simplify the expression with the rules above and then calculate the result.

I searched the web briefly to find libraries to do that and didn't find any yet. But I'm sure some can be found for other languages/platforms or even .NET.

Please note that the above approach in the end just makes a better result and yet does not solve this problem inherent in the nature of numeric computations.

Altri suggerimenti

As you have mentioned, result of calculation with the usage of floating-point numbers have to be 'fitted' into floating-point representation. It's why exact comparison is not a good idea - some tolerance is required. So instead x == y, Math.Abs(x - y) < tolerance should be used.

Welcome to the pain of floating point arithmetic.

What you really want, of course, is a rational number class and library. Here's a start of one in C#, though I don't know how complete it is (probably not very):

http://www.codeproject.com/Articles/88980/Rational-Numbers-NET-4-0-Version-Rational-Computin

There are some in C/C++, but again, don't know how useful/complete they are.

For floating point:

Take a look at some resources. First, David Goldberg's classic, "What Every Computer Scientist Should Know About Floating-Point Arithmetic". Here's the abstract:

Floating-point arithmetic is considered as esoteric subject by many people. This is rather surprising, because floating-point is ubiquitous in computer systems: Almost every language has a floating-point datatype; computers from PCs to supercomputers have floating-point accelerators; most compilers will be called upon to compile floating-point algorithms from time to time; and virtually every operating system must respond to floating-point exceptions such as overflow. This paper presents a tutorial on the aspects of floating-point that have a direct impact on designers of computer systems. It begins with background on floating-point representation and rounding error, continues with a discussion of the IEEE floating point standard, and concludes with examples of how computer system builders can better support floating point.

You can download an edited version of the paper, gratis from several places:

Also, the original should be readily available at the big brick building with all the books. The full cite is

David Goldberg. 1991. What every computer scientist should know about floating-point arithmetic. ACM Comput. Surv. 23, 1 (March 1991), 5-48. DOI=10.1145/103162.103163 http://doi.acm.org/10.1145/103162.103163

Then take a look at these resource:

If you go with the way I commented up then storing it into a database will not be a problem too. You can store it with Fraction type in the db. See UDTs.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top