Why Is Floating Point Arithmetic in C# Imprecise

Why is floating point arithmetic in C# imprecise?

Floating point only has so many digits of precision. If you're seeing f1 == f2, it is because any difference requires more precision than a 32-bit float can represent.

I recommend reading What Every Computer Scientist Should Read About Floating Point

Can anyone explain this floating point weirdness to me?

There's a lot of very wooly thinking here. Floating point numbers are not "imprecise". There is no "may". It's a deterministic system, like anything else on a computer.

Don't to analyze what's going on by looking at decimal representations. The source of this behavior is completely obvious if you look at these numbers in binary or hexadecimal. Let's use binary:

float.Epsilon is b1.0 x 2^-149
2.3509887E-38 is b1.0 x 2^-125

If we add these two numbers together, the infinitely precise (unrounded) sum is:

b1.000 0000 0000 0000 0000 0000 1 x 2^-125

Note that the significand of this sum is 25 bits wide (I've grouped the binary digits into sets of four to make them easier to count). This means that it cannot be represented in single-precision, so the result of this sum is not this value, but instead this value rounded to the closes representable float. The two closest representable numbers are:

b1.000 0000 0000 0000 0000 0000 x 2^-125
b1.000 0000 0000 0000 0000 0001 x 2^-125

Our number is exactly halfway in between them. Since you haven't set the rounding mode in your program, we are in the default rounding mode, which is called "round to nearest, ties to even". Because the two options are equally close, the tie is broken by choosing the one whose lowest-order bit is zero. Thus, 2^-125 + 2^-149 is rounded to 2^-125, which is why "it stops increasing".

Floating point arithmetic is too reliable

double x = (0.1 * 3) / 3;
Console.WriteLine("x: {0}", x); // prints "x: 0.1"
Console.WriteLine("x == 0.1: {0}", x == 0.1); // prints "x == 0.1: False"

Remark: based on this don't make the assumption that floating point arithmetic is unreliable in .NET.

Why differs floating-point precision in C# when separated by parantheses and when separated by statements?

I couldn't find a reference to back this up but I think it is due to the following:

  • float operations are calculated in the precision available in the hardware, that means they can be done with a greater precision than that of float.
  • the assignment to the intermediate result2 variable forces rounding back to float precision, but the single expression for rsult1 is computed entirely in native precision before being rounded down.

On a side note, testing float or double with == is always dangerous. The Microsoft Unit testing provides for am Assert.AreEqual(float expected, float actual,float delta) where you would solve this problem with a suitable delta.

Avoid floating point precision issues

An interesting way would be to implement this into the movement function:

float result = //multiply force by time
float modulus = result%0.01f;
result -= modulus; //you will do this in any case
if(modulus>=0.005f) {/*round up. if you want it to only round down, remove
the next 2 lines, if you want it to only round up, remove
the conditional statement*/
result+=0.01f; }

I can't think about how to optimize it further, I removed the else statement and have it take away the modulus without condition as it will be done anyway.

Is floating point math broken?

Binary floating point math is like this. In most programming languages, it is based on the IEEE 754 standard. The crux of the problem is that numbers are represented in this format as a whole number times a power of two; rational numbers (such as 0.1, which is 1/10) whose denominator is not a power of two cannot be exactly represented.

For 0.1 in the standard binary64 format, the representation can be written exactly as

  • 0.1000000000000000055511151231257827021181583404541015625 in decimal, or
  • 0x1.999999999999ap-4 in C99 hexfloat notation.

In contrast, the rational number 0.1, which is 1/10, can be written exactly as

  • 0.1 in decimal, or
  • 0x1.99999999999999...p-4 in an analogue of C99 hexfloat notation, where the ... represents an unending sequence of 9's.

The constants 0.2 and 0.3 in your program will also be approximations to their true values. It happens that the closest double to 0.2 is larger than the rational number 0.2 but that the closest double to 0.3 is smaller than the rational number 0.3. The sum of 0.1 and 0.2 winds up being larger than the rational number 0.3 and hence disagreeing with the constant in your code.

A fairly comprehensive treatment of floating-point arithmetic issues is What Every Computer Scientist Should Know About Floating-Point Arithmetic. For an easier-to-digest explanation, see floating-point-gui.de.

Side Note: All positional (base-N) number systems share this problem with precision

Plain old decimal (base 10) numbers have the same issues, which is why numbers like 1/3 end up as 0.333333333...

You've just stumbled on a number (3/10) that happens to be easy to represent with the decimal system, but doesn't fit the binary system. It goes both ways (to some small degree) as well: 1/16 is an ugly number in decimal (0.0625), but in binary it looks as neat as a 10,000th does in decimal (0.0001)** - if we were in the habit of using a base-2 number system in our daily lives, you'd even look at that number and instinctively understand you could arrive there by halving something, halving it again, and again and again.

** Of course, that's not exactly how floating-point numbers are stored in memory (they use a form of scientific notation). However, it does illustrate the point that binary floating-point precision errors tend to crop up because the "real world" numbers we are usually interested in working with are so often powers of ten - but only because we use a decimal number system day-to-day. This is also why we'll say things like 71% instead of "5 out of every 7" (71% is an approximation, since 5/7 can't be represented exactly with any decimal number).

So no: binary floating point numbers are not broken, they just happen to be as imperfect as every other base-N number system :)

Side Side Note: Working with Floats in Programming

In practice, this problem of precision means you need to use rounding functions to round your floating point numbers off to however many decimal places you're interested in before you display them.

You also need to replace equality tests with comparisons that allow some amount of tolerance, which means:

Do not do if (x == y) { ... }

Instead do if (abs(x - y) < myToleranceValue) { ... }.

where abs is the absolute value. myToleranceValue needs to be chosen for your particular application - and it will have a lot to do with how much "wiggle room" you are prepared to allow, and what the largest number you are going to be comparing may be (due to loss of precision issues). Beware of "epsilon" style constants in your language of choice. These are not to be used as tolerance values.

What's the benefit of accepting floating point inaccuracy in c#

Why does c# accept the inaccuracy by using floating points to store data?

"C#" doesn't accept the tradeoff of performance over accuracy; users do, or do not, accept that.

C# has three floating point types - float, double and decimal - because those three types meet the vast majority of the needs of real-world programmers.

float and double are good for "scientific" calculations where an answer that is correct to three or four decimal places is always close enough, because that's the precision that the original measurement came in with. Suppose you divide 10.00 by 3 and get 3.333333333333. Since the original measurement was probably accurate to only 0.01, the fact that the computed result is off by less than 0.0000000000004 is irrelevant. In scientific calculations, you're not representing known-to-be-exact quantities. Imprecision in the fifteenth decimal place is irrelevant if the original measurement value was only precise to the second decimal place.

This is of course not true of financial calculations. The operands to a financial calculation are usually precise to two decimal places and represent exact quantities. Decimal is good for "financial" calculations because decimal operation results are exact provided that all of the inputs and outputs can be represented exactly as decimals (and they are all in a reasonable range). Decimals still have rounding errors, of course, but the operations which are exact are precisely those that you are likely to want to be exact when doing financial calculations.

And what's the benefit of using it over other methods?

You should state what other methods you'd like to compare against. There are a great many different techniques for performing calculations on computers.

For example, Math.Pow(Math.Sqrt(2),2) is not exact in c#. There are programming languages that can calculate it exactly (for example, Mathematica).

Let's be clear on this point; Mathematica does not "calculate" root 2 exactly; the number is irrational, so it cannot be calculated exactly in any finite amount of storage. Instead, what mathematica does is it represents numbers as objects that describe how the number was produced. If you say "give me the square root of two", then Mathematica essentially allocates an object that means "the application of the square root operator to the exact number 2". If you then square that, it has special purpose logic that says "if you square something that was the square root of something else, give back the original value". Mathematica has objects that represent various special numbers as well, like pi or e, and a huge body of rules for how various manipulations of those numbers combine together.

Basically, it is a symbolic system; it manipulates numbers the same way people do when they do pencil-and-paper math. Most computer programs manipulate numbers like a calculator: perform the calculation immediately and round it off. If that is not acceptable then you should stick to a symbolic system.

One argument I could think of is that calculating it exactly is a lot slower then just coping with the inaccuracy, but Mathematica & Matlab are used to calculate gigantic scientific problems, so I find it hard to believe those languages are really significantly slower than c#.

It's not that they're slower, though multiplication of floating points really is incredibly fast on modern hardware. It's that the symbolic calculation engine is immensely complex. It encodes all the rules of basic mathematics, and there are a lot of those rules! C# is not intended to be a professional-grade symbolic computation engine, it's intended to be a general-purpose programming language.

Why am I getting the wrong result when using float?

I guess you are refering to deviations cause by Floating point arithmetics. You can read about in in the provided link.

If you really need to make the calculation 100% accurate, you can use decimal instead of float.



Related Topics



Leave a reply



Submit