简体   繁体   中英

Why does this C# floating point comparison fail only in 32 bit?

I am having very different results when comparing, what seems like identical floating point comparison statements. What is bizarre, both statements are true in 64-bit, and only in 32-bit are the results not equal.

Even if I explicitly cast the '132' and/or 'initial' to an Int32, the result in 32-bit is still the same.

int initial = 134;
float initialConverted = initial/255.0f;

// both are true in 64, abs2 is false in 32
var abs = Math.Abs(initialConverted - (134/255.0f)) < float.Epsilon;
var abs2 = Math.Abs(initialConverted - (initial/255.0f)) < float.Epsilon;

Why is there a problem with division when the integer value is stored in its own field?

This is just a variant of the normal floating point comparison and accuracy problems.

Floating point calculations are slightly different in 32-bit and 64-bit and slightly different between DEBUG and RELEASE builds. Most likely in one setting it evaluates to 0, in another to something equal to or slightly larger than float.Epsilon .

I would not use float.Epsilon , it is far too small to handle normal inaccuracies. Instead you need to decide on an epsilon value yourself, that would be "close enough".

float.Epsilon is the same as Single.Epsilon , which is documented as:

Represents the smallest positive Single value that is greater than zero. This field is constant.

In other words, this is just the smallest number representable in the Single data type, it is not usable to handle inaccuracies in calculations, you need something larger for that. Single.Epsilon is somewhere in the vicinity of 1.4E-45 which doesn't allow any inaccuracies at all.

There are a couple of things going on here.

Firstly, C# uses a different definition of epsilon than other languages. float.Epsilon is the next largest float after 0. Due to the scaling property of floating point numbers, this is very small ( 1.0e-45f assuming standard IEEE754 binary32 format). In most other languages (such as say, FLT_EPSILON in C ), epsilon refers to the difference between 1 and the next largest float ( 1.1920929e-7f in binary32 format).

What this means is that the threshold you're using is very tight, too tight too allow for the usual floating point rounding error.

The reason for the difference between archiectures is due to differences in handling intermediate precision . On a modern CPU, there are two sets of instructions for handling floating point numbers:

  • x87 instructions: these date back to the original 8086 processors (or, more specifically, the 8087 coprocessors that accompanied them). They internally utilise a higher precision than the format, namely an 80-bit format (compared with typical 32-bit floats and 64-bit doubles). However at certain steps, operations will need to be truncated to the destination format. The precise rules for when this occurs depends on the language (see here for C#). This is the reason why your abs2 is false on a 32-bit machine: initialConverted has been rounded to a float , but the second (initial/255.0f) has not (I'm not sure why this doesn't occur in abs , but I guess the compiler optimises away the constant expression (134/255.0f) into a float ).

  • SSE instructions: these were introduced as "fast-but-restrictive" floating point operations for games and multimedia, but now have almost completely supplanted the x87 instructions on modern processors. Unlike x87, there is no extended precision (so a float - float immediately returns a float ), they are faster, and offer basic parallelism via SIMD operations. They are almost certainly being used on a 64-bit machine (they are also available on most 32-bit machines from the past decade, but compilers tend not to use them, I guess for compatibility reasons). As there is no extended precision, initialConverted and (initial/255.0f) will both be identical float s, hence abs2 is true.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM