Blog

Equality Comparison of Floating-Point Numbers in C# (and Others)

tl;dr

You should compare two floating-point numbers for equality in C# as follows:

tolerance must be much larger than double.Epsilon.

The problem in the equality operator

You should avoid using the equality operator == to compare two floating-point numbers for equality. This is because the equality operator can return false for two intuitively equal numbers. The following list and the output indicate the problem.

The output:
x: 0.099999999999999978, y: 0.100000000000000006,
    x == y: False, Abs(x-y): 0.000000000000000028
x: 0.299999999999999989, y: 0.300000000000000044,
    x == y: False, Abs(x-y): 0.000000000000000056
x: 0.999999999999999889, y: 1.000000000000000000,
    x == y: False, Abs(x-y): 0.000000000000000111

The equality comparisons between 1.0 - 0.9 and 0.1, between 0.15 + 0.15 and 0.1 + 0.2, and the sum of ten 0.1 and 1.0 unexpectedly result in false. The errors in the floating-point numbers cause these results.

Tolerating absolute errors

The well-known way to resolve this problem is to compare the absolute error of two numbers with a minuscule number. The following results are as expected because the absolute errors are less than 1e-10.

The output:
x: 0.099999999999999978, y: 0.100000000000000006,
    TolerateAbsError: True, Abs(x-y): 0.000000000000000028
x: 0.299999999999999989, y: 0.300000000000000044,
    TolerateAbsError: True, Abs(x-y): 0.000000000000000056
x: 0.999999999999999889, y: 1.000000000000000000,
    TolerateAbsError: True, Abs(x-y): 0.000000000000000111

This approach has a problem where the absolute error between two large numbers can easily exceed the minuscule number. For example, the comparison between 0.1 added to 1e6 ten times and 1e6 plus 1.0 results in false because the absolute error exceeds 1e-10.

The output:
x: 1000000.999999999767169356, y: 1000001.000000000000000000,
    TolerateAbsError: False, Abs(x-y): 0.000000000232830644

Tolerating relative errors

You can resolve this problem by tolerating the relative error instead of the absolute error. A relative error of two numbers is the absolute error divided by the maximum of their absolute values.

The following comparison tolerant of the relative error between the two additions to 1e6 returns true because the relative error is less than 1e-10. Please notice a / b <= c forms a <= b * c to avoid division by zero.

The output:
x: 1000000.999999999767169356, y: 1000001.000000000000000000,
    TolerantRelativeError: True, RelativeError: 0.000000000000000233

This technique causes another problem. The relative error between two minuscule numbers almost equal to zero can become very large. For example, the comparison tolerant of the relative error between 1e-11 and 1e-12 returns false.

The output:

x: 0.000000000010000000, y: 0.000000000001000000,
    TolerantRelativeError: False, RelativeError: 0.900000000000000022

Torelating both errors

The equality comparison must tolerate both the absolute and relative errors of two numbers to solve those problems.

The above equality comparison returns true for both of the corner cases.

The output:
x: 1000000.999999999767169356, y: 1000001.000000000000000000,
    TolerateRelativeAndAbsError: True
x: 0.000000000010000000, y: 0.000000000001000000,
    TolerateRelativeAndAbsError: True

The myth of machine epsilon

I have arbitrarily chosen 1e-10 as the tolerance so far, but there is a mathematically rigid number misunderstood as an appropriate value in this case. It is machine epsilon.

Many programming languages define machine epsilon as the difference between 1 and the next larger floating-point number, even though the formal definition is different from it. In C#, Double.Epsilon has machine epsilon.

You can't use machine epsilon as the tolerance. When adopting Double.Epsilon as the tolerance, the comparison between the sum of ten 0.1 and 1.0 in the first example returns false. Machine epsilon is too small, and cumulative errors of arithmetic operations easily exceed it.

The output:
x: 0.999999999999999889, y: 1.000000000000000000,
    UseMachineEpsilonAsTolerance: False

After all, the tolerance should be based on the precision necessary to your application. It also should be much larger than machine epsilon.

Conclusion

The equality comparison of floating-point numbers should take into account both the absolute and relative errors. Furthermore, the error tolerance in the comparison should be chosen based on the required accuracy in your application. If you would like to know more details of this topic, you should consult Comparing Floating Point Numbers, 2012 Edition.

This GitHub repository has the above examples and the implementation of EqualityComparer<double> and IComperer<double> based on this article.

Implementing a System Tray App with WPF and MVVM

This article illustrates the implementation of a system tray application with WPF and the MVVM pattern. The full source code is in the GitHub repository.

The implementation has two distinctive points. First, it does not use notable WPF NotifyIcon because the license, CPOL, isn't compatible with any OSS licenses. Then, the implementation obeys the MVVM pattern and has no code behind.

Continue reading "Implementing a System Tray App with WPF and MVVM"

Quite Simple Memory Pool in C#

This article shows a quite simple memory pool to make a thread-unsafe library thread-safe without performance degradation in single-threaded programs. Same as the previous article, this article is about DynaJson.

Thread-safety requires overhead to allocate an instance by each invocation to isolate data being altered. Unless thread-safety is required, we can use a static class or a singleton to eliminate any additional allocation.

Continue reading "Quite Simple Memory Pool in C#"

[Unity] How to Use Physics.OverlapCapsule

Capsule and Cube Collider

You can use Physics.OverlapCapsule to get two Colliders overlapped with a CapsulCollider as above. This method takes the position and the size of a capsule and returns all Colliders overlapped by the capsule.

You can get the Colliders by configuring a Kinematic Rigidbody Trigger Collier and handling the OnTriggerEnter message, but the method is handy.

Continue reading "[Unity] How to Use Physics.OverlapCapsule"

How to Make JSON Parser Strict

I developed a JSON Parser for C# named DynaJson. It is very strict to the standard of RFC 8259. It accepts all conformant and rejects all non-conformant JSONs except for two exceptions.

One exception is trailing-commas. Another is leading 0 in numbers, for example, 02 and -02. The former is for practicality. The latter is for compatibility with DynamicJson.

JSON's grammar is simple, but carelessly implemented parsers don't accept all conformant and not reject non-conformant JSONs. An excellent article of Parsing JSON is a Minefield shows where mines are in implementing JSON parsers.

Continue reading "How to Make JSON Parser Strict"

Mount Any Windows Folder into Containers on Docker for Windows

In a native Docker environment, you can mount /source in a container host onto /destination in a container by docker run -v /source:/destination and access it from the container.

Well then, how can you mount C:\Source in Windows onto /destination in a container on Docker for Windows? You can't directly mount C:\Source in the VM host into the container, of course.

As the first step, you have to set up C:\Source as a shared folder for a VM when you create it with docker-machine. You can specify the shared folder with the --virtualbox-share-folder option of the VirtualBox deriver as follows.

docker-machine create --driver virtualbox --virtualbox-share-folder=C:\Source:Source

Continue reading "Mount Any Windows Folder into Containers on Docker for Windows"