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 improvement of comparison function

+59 -48
+52 -41
content/errors/NearlyEqualsTest.java
··· 4 4 import org.junit.Test; 5 5 6 6 /** 7 - * Test suite to demonstrate a good method for comparing 8 - * floating-point values using an epsilon. Run via JUnit 4. 7 + * Test suite to demonstrate a good method for comparing floating-point values using an epsilon. Run via JUnit 4. 9 8 * 10 - * Note: this function attempts a "one size fits all" solution. There 11 - * may be some edge cases for which it still produces unexpected results, 12 - * and some of the tests it was developed to pass probably specify behaviour 13 - * that is not appropriate for some applications. Before using it, make 14 - * sure it's appropriate for your application! 9 + * Note: this function attempts a "one size fits all" solution. There may be some edge cases for which it still 10 + * produces unexpected results, and some of the tests it was developed to pass probably specify behaviour that is 11 + * not appropriate for some applications. Before using it, make sure it's appropriate for your application! 15 12 * 16 13 * From http://floating-point-gui.de 17 14 * 18 15 * @author Michael Borgwardt 19 16 */ 20 - public class NearlyEqualsTest 21 - { 22 - public static boolean nearlyEqual(float a, float b, float epsilon) 23 - { 17 + public class NearlyEqualsTest { 18 + public static boolean nearlyEqual(float a, float b, float epsilon) { 24 19 final float absA = Math.abs(a); 25 20 final float absB = Math.abs(b); 26 - final float diff = Math.abs(a-b); 21 + final float diff = Math.abs(a - b); 27 22 28 - if (a*b==0) { // a or b or both are zero 23 + if (a * b == 0) { // a or b or both are zero 29 24 // relative error is not meaningful here 30 - return diff < Float.MIN_VALUE / epsilon; 25 + return diff < (epsilon * epsilon); 31 26 } else { // use relative error 32 - return diff / (absA+absB) < epsilon; 27 + return diff / (absA + absB) < epsilon; 33 28 } 34 29 } 35 30 36 - public static boolean nearlyEqual(float a, float b) 37 - { 31 + public static boolean nearlyEqual(float a, float b) { 38 32 return nearlyEqual(a, b, 0.000001f); 39 33 } 40 34 41 35 /** Regular large numbers - generally not problematic */ 42 - @Test public void big() 43 - { 36 + @Test 37 + public void big() { 44 38 assertTrue(nearlyEqual(1000000f, 1000001f)); 45 39 assertTrue(nearlyEqual(1000001f, 1000000f)); 46 40 assertFalse(nearlyEqual(10000f, 10001f)); ··· 48 42 } 49 43 50 44 /** Negative large numbers */ 51 - @Test public void bigNeg() 52 - { 45 + @Test 46 + public void bigNeg() { 53 47 assertTrue(nearlyEqual(-1000000f, -1000001f)); 54 48 assertTrue(nearlyEqual(-1000001f, -1000000f)); 55 49 assertFalse(nearlyEqual(-10000f, -10001f)); ··· 57 51 } 58 52 59 53 /** Numbers around 1 */ 60 - @Test public void mid() 61 - { 54 + @Test 55 + public void mid() { 62 56 assertTrue(nearlyEqual(1.0000001f, 1.0000002f)); 63 57 assertTrue(nearlyEqual(1.0000002f, 1.0000001f)); 64 58 assertFalse(nearlyEqual(1.0002f, 1.0001f)); ··· 66 60 } 67 61 68 62 /** Numbers around -1 */ 69 - @Test public void midNeg() 70 - { 63 + @Test 64 + public void midNeg() { 71 65 assertTrue(nearlyEqual(-1.000001f, -1.000002f)); 72 66 assertTrue(nearlyEqual(-1.000002f, -1.000001f)); 73 67 assertFalse(nearlyEqual(-1.0001f, -1.0002f)); ··· 75 69 } 76 70 77 71 /** Numbers between 1 and 0 */ 78 - @Test public void small() 79 - { 72 + @Test 73 + public void small() { 80 74 assertTrue(nearlyEqual(0.000000001000001f, 0.000000001000002f)); 81 75 assertTrue(nearlyEqual(0.000000001000002f, 0.000000001000001f)); 82 76 assertFalse(nearlyEqual(0.000000000001002f, 0.000000000001001f)); ··· 84 78 } 85 79 86 80 /** Numbers between -1 and 0 */ 87 - @Test public void smallNeg() 88 - { 81 + @Test 82 + public void smallNeg() { 89 83 assertTrue(nearlyEqual(-0.000000001000001f, -0.000000001000002f)); 90 84 assertTrue(nearlyEqual(-0.000000001000002f, -0.000000001000001f)); 91 85 assertFalse(nearlyEqual(-0.000000000001002f, -0.000000000001001f)); ··· 93 87 } 94 88 95 89 /** Comparisons involving zero */ 96 - @Test public void zero() 97 - { 90 + @Test 91 + public void zero() { 98 92 assertTrue(nearlyEqual(0.0f, 0.0f)); 93 + assertTrue(nearlyEqual(0.0f, -0.0f)); 94 + assertTrue(nearlyEqual(-0.0f, -0.0f)); 99 95 assertFalse(nearlyEqual(0.00000001f, 0.0f)); 100 96 assertFalse(nearlyEqual(0.0f, 0.00000001f)); 97 + assertFalse(nearlyEqual(-0.00000001f, 0.0f)); 98 + assertFalse(nearlyEqual(0.0f, -0.00000001f)); 99 + 100 + assertTrue(nearlyEqual(0.0f, 0.00000001f, 0.01f)); 101 + assertTrue(nearlyEqual(0.00000001f, 0.0f, 0.01f)); 102 + assertFalse(nearlyEqual(0.00000001f, 0.0f, 0.000001f)); 103 + assertFalse(nearlyEqual(0.0f, 0.00000001f, 0.000001f)); 104 + 105 + assertTrue(nearlyEqual(0.0f, -0.00000001f, 0.1f)); 106 + assertTrue(nearlyEqual(-0.00000001f, 0.0f, 0.1f)); 107 + assertFalse(nearlyEqual(-0.00000001f, 0.0f, 0.00000001f)); 108 + assertFalse(nearlyEqual(0.0f, -0.00000001f, 0.00000001f)); 101 109 } 102 110 103 111 /** Comparisons of numbers on opposite sides of 0 */ 104 - @Test public void opposite() 105 - { 112 + @Test 113 + public void opposite() { 106 114 assertFalse(nearlyEqual(1.000000001f, -1.0f)); 107 115 assertFalse(nearlyEqual(-1.0f, 1.000000001f)); 108 116 assertFalse(nearlyEqual(-1.000000001f, 1.0f)); 109 117 assertFalse(nearlyEqual(1.0f, -1.000000001f)); 110 - assertTrue(nearlyEqual(10000f*Float.MIN_VALUE, -10000f*Float.MIN_VALUE)); 118 + assertTrue(nearlyEqual(1e10f * Float.MIN_VALUE, -1e10f * Float.MIN_VALUE)); 111 119 } 112 120 113 121 /** 114 - * The really tricky part - comparisons of numbers 115 - * very close to zero. 122 + * The really tricky part - comparisons of numbers very close to zero. 116 123 */ 117 - @Test public void ulp() 118 - { 124 + @Test 125 + public void ulp() { 119 126 assertTrue(nearlyEqual(Float.MIN_VALUE, -Float.MIN_VALUE)); 120 127 assertTrue(nearlyEqual(-Float.MIN_VALUE, Float.MIN_VALUE)); 121 128 assertTrue(nearlyEqual(Float.MIN_VALUE, 0)); ··· 128 135 assertFalse(nearlyEqual(Float.MIN_VALUE, 0.000000001f)); 129 136 assertFalse(nearlyEqual(-Float.MIN_VALUE, 0.000000001f)); 130 137 131 - assertFalse(nearlyEqual(1e20f*Float.MIN_VALUE, 0.0f)); 132 - assertFalse(nearlyEqual(0.0f, 1e20f*Float.MIN_VALUE)); 133 - assertFalse(nearlyEqual(1e20f*Float.MIN_VALUE, -1e20f*Float.MIN_VALUE)); 138 + assertFalse(nearlyEqual(1e25f * Float.MIN_VALUE, 0.0f, 1e-12f)); 139 + assertFalse(nearlyEqual(0.0f, 1e25f * Float.MIN_VALUE, 1e-12f)); 140 + assertFalse(nearlyEqual(1e25f * Float.MIN_VALUE, -1e25f * Float.MIN_VALUE, 1e-12f)); 141 + 142 + assertTrue(nearlyEqual(1e25f * Float.MIN_VALUE, 0.0f, 1e-5f)); 143 + assertTrue(nearlyEqual(0.0f, 1e25f * Float.MIN_VALUE, 1e-5f)); 144 + assertTrue(nearlyEqual(1e20f * Float.MIN_VALUE, -1e20f * Float.MIN_VALUE, 1e-5f)); 134 145 } 135 146 136 147 }
+7 -7
content/errors/comparison.html
··· 40 40 41 41 public static boolean nearlyEqual(float a, float b, float epsilon) 42 42 { 43 - float absA = Math.abs(a); 44 - float absB = Math.abs(b); 45 - float diff = Math.abs(a-b); 43 + final float absA = Math.abs(a); 44 + final float absB = Math.abs(b); 45 + final float diff = Math.abs(a - b); 46 46 47 - if (a*b==0) { // a or b or both are zero 47 + if (a * b == 0) { // a or b or both are zero 48 48 // relative error is not meaningful here 49 - return diff < Float.MIN_VALUE / epsilon; 49 + return diff < (epsilon * epsilon); 50 50 } else { // use relative error 51 - return diff / (absA+absB) < epsilon; 51 + return diff / (absA + absB) < epsilon; 52 52 } 53 53 } 54 54 ··· 56 56 uses some quite non-obvious logic. In particular, it has to use a completely different definition of error margin 57 57 when `a` or `b` is zero, because the classical definition of relative error becomes meaningless in those cases. 58 58 59 - There probably are some cases where the method above still produces unexpected results, and some of the tests 59 + There are some cases where the method above still produces unexpected results (in particular, it's much stricter when one value is nearly zero than when it is exactly zero), and some of the tests 60 60 it was developed to pass probably specify behaviour that is not appropriate for some applications. Before using it, make sure it's appropriate for your application! 61 61 62 62 Comparing floating-point values as integers