F# - How to compare floats

后端 未结 2 1268
逝去的感伤
逝去的感伤 2021-01-25 08:48

In F#. How to efficiently compare floats for equality that are almost equal? It should work for very large and very small values too. I am thinking of first comparing the Expone

相关标签:
2条回答
  • 2021-01-25 09:22

    When you ask how to compare floating-point values that are almost equal, you are asking:

    • I have two values, x and y, that have been computed with floating-point arithmetic, so they contain rounding errors and are approximations of ideal mathematical values x and y. How can I use the floating-point x and y to compare the mathematical x and y for equality?

    There are two problems here:

    1. We do not know how much error there may be in x or y. Some combinations of arithmetic magnify errors, while others shrink them. It is possible for the errors in x and y to range from zero to infinity, and you have not given us any information about this.
    2. It is often assumed that the goal is to produce a result of “equal” when x and y are unequal but close to each other. This converts false negatives (inequality would be reported even though the mathematical x and y would be equal) into positives. However, it creates false positives (equality is reported even though the mathematical x and y would be unequal).

    There is no general solution for these problems.

    • It is impossible to know in general whether an application can tolerate being told that values are equal when they should be unequal or vice-versa without knowing specific details about that application.
    • It is impossible to know in general how much error there may be in x and y.

    Therefore, there is no correct general test for equality in values that have been computed appoximately.

    Note that this problem is not really about testing for equality. Generally, it is impossible to compute any function of incorrect data (except for trivial functions such as constant functions). Since x and y contain errors, it is impossible to use x to compute log(x) without errors, or to compute arcosine(y) or sqrt(x) without errors. In fact, if the errors have made y slightly greater than 1 while y is not or made x slightly less than zero while x is not, then computing acos(y) or sqrt(x) will produce exceptions and NaNs even though the ideal mathematical values would work without problem.

    What this all means is that you cannot simply convert exact mathematical arithmetic to approximate floating-point arithmetic and expect to get a good result (whether you are testing for equality or not). You must consider the effects of converting exact arithmetic to approximate arithmetic and evaluate how they affect your program and your data. The use of floating-point arithmetic, including comparisons for equality, must be tailored to individual situations.

    0 讨论(0)
  • 2021-01-25 09:31

    An F# float is just a shorthand for System.Double. That being the case, you can use the BitConverter.DoubleToInt64Bits method to efficiently (and safely!) "cast" an F# float value to int64; this is useful because it avoids allocating a byte[], as John mentioned in his comment. You can get the exponent and significand from that int64 using some simple bitwise operations.

    As John said though, you're probably better off with a simple check for relative accuracy. It's likely to be the fastest solution and "close enough" for many use cases (e.g., checking to see if an iterative solver has converged on a solution). If you need a specific amount of accuracy, take a look at NUnit's code -- it has some nice APIs for asserting that values are within a certain percentage or number of ulps of an expected value.

    0 讨论(0)
提交回复
热议问题