diff options
author | Jean-Luc Brouillet <jeanluc@google.com> | 2014-07-24 00:53:15 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2014-07-23 18:04:01 +0000 |
commit | 762716417cc8115b36ee6896f384faa313943262 (patch) | |
tree | 1a469df34f4f293f14b404dfe7754e588f622c77 | |
parent | 1df878568c84a1510ff1a96506b3f4bb96b7269b (diff) | |
parent | 00c565bac18bb38784b077a7be6588efa7b93fbc (diff) | |
download | cts-762716417cc8115b36ee6896f384faa313943262.tar.gz |
Merge "Fix CTS tests precision issues."
-rw-r--r-- | tests/tests/renderscript/src/android/renderscript/cts/CoreMathVerifier.java | 42 | ||||
-rw-r--r-- | tests/tests/renderscript/src/android/renderscript/cts/Floaty.java | 88 |
2 files changed, 113 insertions, 17 deletions
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/CoreMathVerifier.java b/tests/tests/renderscript/src/android/renderscript/cts/CoreMathVerifier.java index 90f47dae643..f378ea4c69f 100644 --- a/tests/tests/renderscript/src/android/renderscript/cts/CoreMathVerifier.java +++ b/tests/tests/renderscript/src/android/renderscript/cts/CoreMathVerifier.java @@ -229,7 +229,9 @@ public class CoreMathVerifier { new Floaty(point2[i], ulpFactor, ulpRelaxedFactor)); sum.add(Floaty.multiply(diff, diff)); } - return Floaty.sqrt(sum); + Floaty d = Floaty.sqrt(sum); + d.setMinimumError(ulpFactor, ulpRelaxedFactor); + return d; } // Returns the length of the n-dimensional vector. @@ -239,7 +241,9 @@ public class CoreMathVerifier { Floaty f = new Floaty(array[i], ulpFactor, ulpRelaxedFactor); sum.add(Floaty.multiply(f, f)); } - return Floaty.sqrt(sum); + Floaty l = Floaty.sqrt(sum); + l.setMinimumError(ulpFactor, ulpRelaxedFactor); + return l; } // Normalizes the n-dimensional vector, i.e. makes it length 1. @@ -255,7 +259,8 @@ public class CoreMathVerifier { } // Computes the cross product of two 3D vectors. - static private void cross(float[] v1, float[] v2, Floaty[] out) { + static private void cross(float[] v1, float[] v2, Floaty[] out, int ulpFactor, + int ulpRelaxedFactor) { Floaty a12 = Floaty.multiply(new Floaty(v1[1]), new Floaty(v2[2])); Floaty a21 = Floaty.multiply(new Floaty(v1[2]), new Floaty(v2[1])); out[0] = Floaty.subtract(a12, a21); @@ -268,6 +273,14 @@ public class CoreMathVerifier { if (out.length == 4) { out[3] = new Floaty(0f); } + setMinimumError(out, ulpFactor, ulpRelaxedFactor); + } + + // Set a minimum error for every entry of out. + static void setMinimumError(Floaty[] out, int ulpFactor, int ulpRelaxedFactor) { + for (int i = 0; i < out.length; i++) { + out[i].setMinimumError(ulpFactor, ulpRelaxedFactor); + } } static public void computeAbs(TestAbs.ArgumentsCharUchar args) { @@ -756,11 +769,14 @@ public class CoreMathVerifier { } static public void computeCospi(TestCospi.ArgumentsFloatFloat args) { - args.out = new Floaty(cos(args.in * (float) Math.PI), 4, 128); + Floaty ip = new Floaty((float) ((double)args.in * Math.PI), 1, 1); + args.out = Floaty.FloatyFromRange( + (float) Math.cos(ip.getDoubleMin()), + (float) Math.cos(ip.getDoubleMax()), 4, 128); } static public void computeCross(TestCross.ArgumentsFloatNFloatNFloatN args) { - cross(args.inLhs, args.inRhs, args.out); + cross(args.inLhs, args.inRhs, args.out, 1, 4); } static public void computeDegrees(TestDegrees.ArgumentsFloatFloat args) { @@ -777,6 +793,7 @@ public class CoreMathVerifier { static public void computeDot(TestDot.ArgumentsFloatFloatFloat args) { args.out = new Floaty(args.inLhs * args.inRhs); + args.out.setMinimumError(1, 4); } static public void computeDot(TestDot.ArgumentsFloatNFloatNFloat args) { @@ -787,6 +804,7 @@ public class CoreMathVerifier { sum.add(Floaty.multiply(a, b)); } args.out = sum; + args.out.setMinimumError(1, 4); } static public void computeErf(TestErf.ArgumentsFloatFloat args) { @@ -847,7 +865,7 @@ public class CoreMathVerifier { } static public void computeFdim(TestFdim.ArgumentsFloatFloatFloat args) { - args.out = new Floaty(Math.max(0f, args.inA - args.inB), 0, 0); + args.out = new Floaty(Math.max(0f, args.inA - args.inB), 0, 1); } static public void computeFloor(TestFloor.ArgumentsFloatFloat args) { @@ -960,6 +978,7 @@ public class CoreMathVerifier { static public void computeMad(TestMad.ArgumentsFloatFloatFloatFloat args) { args.out = Floaty.add(new Floaty(args.inA * args.inB), new Floaty(args.inC)); + args.out.setMinimumError(1, 4); } static public void computeMax(TestMax.ArgumentsCharCharChar args) { @@ -1043,6 +1062,7 @@ public class CoreMathVerifier { Floaty stop = new Floaty(args.inStop); Floaty diff = Floaty.subtract(stop, start); args.out = Floaty.add(start, Floaty.multiply(diff, new Floaty(args.inAmount))); + args.out.setMinimumError(1, 4); } static public void computeModf(TestModf.ArgumentsFloatFloatFloat args) { @@ -1183,7 +1203,10 @@ public class CoreMathVerifier { } static public void computeSinpi(TestSinpi.ArgumentsFloatFloat args) { - args.out = new Floaty(sin(args.in * (float) Math.PI), 4, 128); + Floaty ip = new Floaty((float) ((double)args.in * Math.PI), 1, 1); + args.out = Floaty.FloatyFromRange( + (float) Math.sin(ip.getDoubleMin()), + (float) Math.sin(ip.getDoubleMax()), 4, 128); } static public void computeSqrt(TestSqrt.ArgumentsFloatFloat args) { @@ -1203,7 +1226,10 @@ public class CoreMathVerifier { } static public void computeTanpi(TestTanpi.ArgumentsFloatFloat args) { - args.out = new Floaty(tan(args.in * (float) Math.PI), 6, 128); + Floaty ip = new Floaty((float) ((double)args.in * Math.PI), 1, 1); + args.out = Floaty.FloatyFromRange( + (float) Math.tan(ip.getDoubleMin()), + (float) Math.tan(ip.getDoubleMax()), 4, 128); } static public void computeTgamma(TestTgamma.ArgumentsFloatFloat args) { diff --git a/tests/tests/renderscript/src/android/renderscript/cts/Floaty.java b/tests/tests/renderscript/src/android/renderscript/cts/Floaty.java index 608e172ff94..85b00cee529 100644 --- a/tests/tests/renderscript/src/android/renderscript/cts/Floaty.java +++ b/tests/tests/renderscript/src/android/renderscript/cts/Floaty.java @@ -24,8 +24,12 @@ import android.util.Log; * compute the correct error of the result. */ public class Floaty { - private double mValue; // The value this instance represent. - private double mError; // The real value should be between mValue - mError and mValue + mError. + // The value this instance represents. + private double mValue; + /* The real value should be between mValue - mError and mValue + mError. + * mError should be positive. + */ + private double mError; /* The number of bits the value should have, either 32 or 64. It would have been nice to * use generics, e.g. Floaty<float> and Floaty<double> but Java does not support generics * of float and double. Also, Java does not have a f16 type. This can simulate it, although @@ -57,6 +61,30 @@ public class Floaty { setErrorFromValue(1, 1); } + /** Creates a Floaty for which any value between a and b are legal, expanded by the specified factor. */ + public static Floaty FloatyFromRange(float a, float b, float ulpFactor, float ulpRelaxedFactor) { + // Order the edges of the range. + float min, max; + if (a < b) { + min = a; + max = b; + } else { + min = b; + max = a; + } + // Expand the edges by the specified factor. + float factor = relaxed ? ulpRelaxedFactor : ulpFactor; + min = min + (Math.nextAfter(min, -Float.MAX_VALUE) - min) * factor; + max = max + (Math.nextAfter(max, Float.MAX_VALUE) - max) * factor; + + // Convert a [range] to a (mid +- error) + float delta = Math.abs(a - b); + float mid = (min + max) / 2.f; + Floaty fl = new Floaty(mid); + fl.mError = Math.max(mid - min, max - mid); + return fl; + } + /** Sets the value and the error based on whether we're doing relaxed computations or not. */ public Floaty(float v, int ulpFactor, int ulpRelaxedFactor) { mValue = v; @@ -76,12 +104,17 @@ public class Floaty { public float getFloatError() { return (float) mError; } public double getDoubleError() { return mError; } + public double getDoubleMin() { return mValue - mError; } + public double getDoubleMax() { return mValue + mError; } + /** Returns the number we would need to multiply the ulp to get the current error. */ public int getUlf() { - return (int) Math.abs(mError / getUlp()); + return (int) (mError / getUlp() + 0.5); } - /** Returns the unit of least precision for the number we handle. */ + /** Returns the unit of least precision for the number we handle. This is + * always a positive number. + */ private double getUlp() { if (mNumberOfBits == 64) { return Math.ulp(mValue); @@ -112,7 +145,7 @@ public class Floaty { mError *= relaxed ? ulpRelaxedFactor : ulpFactor; mNumberOfBits = numberOfBits; } - + /** If needed, increases the error so that the provided value is covered by the error range. */ private void expandError(double valueWithError) { // We disregard NaN values that can be produced when testing close to a cliff. @@ -125,6 +158,14 @@ public class Floaty { } } + /** Makes sure the allowed error is at least ulp{Relaxed}Factor. */ + public void setMinimumError(int ulpFactor, int ulpRelaxedFactor) { + double minError = getUlp() * (relaxed ? ulpRelaxedFactor : ulpFactor); + if (mError < minError) { + mError = minError; + } + } + /** Returns true if the number passed is within mError of our value. */ public boolean couldBe(double a) { return couldBe(a, 0.0); @@ -133,7 +174,7 @@ public class Floaty { /** * Returns true if the number passed is within mError of our value, or if it's whithin * minimumError of the value. - */ + */ public boolean couldBe(double a, double minimumError) { if (a != a && mValue != mValue) { return true; // Both are NaN @@ -146,11 +187,40 @@ public class Floaty { double error = Math.max(mError, minimumError); boolean inRange = mValue - error <= a && a <= mValue + error; + /* For relaxed precision, some implementations don't return denormalized values for very + * subnormal values. Two examples: + * a) nextafter(0.0, 1.0): When denormalized are allowed, 1.40129846e-45 (0x00000001) is + * expected. If only normalized values are returned, 1.1754944e-38 (0x00800000) is + * expected. + * b) powr(1600.4, -11.9): With denormalized, 7.4076481e-39 is returned. For normalized, + * 0.0 can be returned. + */ + if (!inRange && relaxed) { + boolean isSubnormal = false; + double normalized = 0.0; + // Check if we have a subnormal value. + if (mNumberOfBits == 32 && Math.abs(mValue) < Float.MIN_NORMAL) { + isSubnormal = true; + normalized = Math.copySign(Float.MIN_NORMAL, (float) mValue); + } else if (Math.abs(mValue) < Double.MIN_NORMAL) { + isSubnormal = true; + normalized = Math.copySign(Double.MIN_NORMAL, mValue); + } + if (isSubnormal) { + // First try replacing the expected value with the larger MIN_NORMAL + inRange = (normalized - error) <= a && a <= (normalized + error); + // Second try replacing the expected value with 0. + if (!inRange) { + inRange = -error <= a && a <= error; + } + } + } + /* This is useful for debugging: if (!inRange) { - int ulfNeeded = (int) Math.abs(Math.round((a - mValue) / Math.ulp(mValue))); - Log.e("Floaty.couldBe", "Comparing " + Float.toString(a) + - " against " + Float.toString(mValue) + " +- " + Float.toString(error) + + int ulfNeeded = (int) Math.abs(Math.round((a - mValue) / getUlp())); + Log.e("Floaty.couldBe", "Comparing " + Double.toString(a) + + " against " + Double.toString(mValue) + " +- " + Double.toString(error) + " relaxed " + Boolean.toString(relaxed) + " ulfNeeded " + Integer.toString(ulfNeeded) + ", off by " + Integer.toString(ulfNeeded - getUlf())); |