Lo que todo programador debería saber sobre aritmética de punto flotante
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Further corrections

+30 -6
+30 -6
content/errors/comparison.html
··· 12 12 b = 0.15 + 0.15 13 13 if(a == b) // can be false! 14 14 15 + Don't use absolute error margins 16 + -------------------------------- 15 17 The solution is to check not whether the numbers are exactly the same, but whether their difference is 16 18 very small. The error margin that the difference is compared to is often called *epsilon*. 17 19 The most simple form: ··· 24 26 Therefore, it is necessary to see whether the *relative error* is smaller than epsilon: 25 27 26 28 if( Math.abs((a-b)/b) < 0.00001 ) // still not right! 27 - 28 - There is one important special case where this will fail: When both a and be are zero. 0.0/0.0 is "not a number", which returns false for all comparisons. So we have to take care of that first: 29 29 30 - if( a==b || Math.abs((a-b)/b) < 0.00001 ) // more correct, but bad design 30 + Look out for edge cases 31 + ----------------------- 32 + There are some important special cases where this will fail: 31 33 32 - Since it's not immediately clear what this code does, it should be done in a function or method: 34 + * When both `a` and `b` are zero. `0.0/0.0` is "not a number", which returns false for all comparisons. 35 + * When only `b` is zero, the division yields "infinity", which is greater than epsilon even when `a` is smaller. 36 + 37 + Also, the result is not commutative (`nearlyEquals(a,b)` is not always the same as `nearlyEquals(b,a)`). To fix these problems, the code has to get a lot more complex, so we really need to put it into a function of its own: 33 38 34 39 function nearlyEqual(a,b) 35 40 { 36 - return a==b || Math.abs((a-b)/b) < 0.00001; 41 + epsilon = 0.00001; 42 + if (a==0.0){ 43 + return Math.abs(b) < epsilon; 44 + else if (b==0.0){ 45 + return Math.abs(a) < epsilon; 46 + } else { // ensure commutativity 47 + return Math.abs((a-b)/a) < epsilon && 48 + Math.abs((b-a)/b) < epsilon; 49 + } 37 50 } 38 51 39 52 if(nearlyEqual(a,b)) 40 53 41 - This is still not a perfect solution. Read the [Comparing floating-point numbers](/references/) paper for more details. 54 + Unfortunately, this is *still* not perfect; there are at least two problems that are not easy to fix: 55 + 56 + * It reverts to using epsilon as an absolute error measure when `a` or `b` is zero. 57 + * It returns `false` when both `a` and `b` are very small but on opposite sides of zero, even when they're the smallest possible non-zero numbers. 58 + 59 + Compare floating-point values as integers 60 + ----------------------------------------- 61 + But the there is an alternative to adding even more conceptual complexity to such an apparently simple task: instead of comparing `a` and `b` as [real numbers](http://en.wikipedia.org/wiki/Real_numbers), we can think about them as discrete steps and define the error margin as the maximum number of possible floating-point values between the two values. 62 + 63 + This is conceptually very clear and easy and has the advantage of implicitly scaling the relative error margin with the magnitude of the values. Technically, it's a bit more complex, but not as much as you might think, because IEEE 754 floats are designed to maintain their order when their bit patters are interpreted as integers. 64 + 65 + However, this method does require the programming language to support conversion between floating-point values and integer bit patters. Read the [Comparing floating-point numbers](/references/) paper for more details.