1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.renderscript.cts; 18 19 import android.util.Log; 20 import java.util.Arrays; 21 import junit.framework.Assert; 22 23 /** 24 * This class and the enclosed Floaty class are used to validate the precision of the floating 25 * point operations of the various drivers. Instances of Target contains information about the 26 * expectations we have for the functions being tested. There's an instance of Floaty for each 27 * floating value being verified. 28 */ 29 public class Target { 30 /** 31 * In relaxed precision mode, we allow: 32 * - less precision in the computation 33 * - using only normalized values 34 * - reordering of the operations 35 * - different rounding mode 36 */ 37 private boolean mIsRelaxedPrecision; 38 39 /* 40 * The following two fields are set just before the expected values are computed for a specific 41 * RenderScript function. Hence, we can't use one instance of this class to test two APIs 42 * in parallel. The generated Test*.java code uses one instance of Target per test, so we're 43 * safe. 44 */ 45 46 /** 47 * For native, we allow the same things as relaxed precision, plus: 48 * - operations don't have to return +/- infinity 49 */ 50 private boolean mIsNative; 51 52 /** 53 * How much we'll allow the values tested to diverge from the values 54 * we compute. This can be very large for native_* and half_* tests. 55 */ 56 private int mUlpFactor; 57 58 Target(boolean relaxed) { 59 mIsRelaxedPrecision = relaxed; 60 } 61 62 /** 63 * Sets whether we are testing a native_* function and how many ulp we allow 64 * for full and relaxed precision. 65 */ 66 void setPrecision(int fullUlpFactor, int relaxedUlpFactor, boolean isNative) { 67 mIsNative = isNative; 68 mUlpFactor = mIsRelaxedPrecision ? relaxedUlpFactor : fullUlpFactor; 69 } 70 71 /** 72 * Helper functions to create a new 32 bit Floaty with the current expected level of precision. 73 * We have variations that expect one to five arguments. Any of the passed arguments are considered 74 * valid values for that Floaty. 75 */ 76 Floaty new32(float a) { 77 return new Floaty(32, new double [] { a }); 78 } 79 80 Floaty new32(float a, float b) { 81 return new Floaty(32, new double [] { a, b }); 82 } 83 84 Floaty new32(float a, float b, float c) { 85 return new Floaty(32, new double [] { a, b, c }); 86 } 87 88 Floaty new32(float a, float b, float c, float d) { 89 return new Floaty(32, new double [] { a, b, c, d }); 90 } 91 92 Floaty new32(float a, float b, float c, float d, float e) { 93 return new Floaty(32, new double [] { a, b, c, d, e }); 94 } 95 96 /** 97 * Helper functions to create a new 64 bit Floaty with the current expected level of precision. 98 * We have variations that expect one to five arguments. Any of the passed arguments are considered 99 * valid values for that Floaty. 100 */ 101 Floaty new64(double a) { 102 return new Floaty(64, new double [] { a }); 103 } 104 105 Floaty new64(double a, double b) { 106 return new Floaty(64, new double [] { a, b }); 107 } 108 109 Floaty new64(double a, double b, double c) { 110 return new Floaty(64, new double [] { a, b, c }); 111 } 112 113 Floaty new64(double a, double b, double c, double d) { 114 return new Floaty(64, new double [] { a, b, c, d }); 115 } 116 117 Floaty new64(double a, double b, double c, double d, double e) { 118 return new Floaty(64, new double [] { a, b, c, d, e }); 119 } 120 121 /** 122 * Returns a Floaty that contain a NaN for the specified size. 123 */ 124 Floaty newNan(int numberOfBits) { 125 return new Floaty(numberOfBits, new double [] { Double.NaN }); 126 } 127 128 Floaty add(Floaty a, Floaty b) { 129 //Log.w("Target.add", "a: " + a.toString()); 130 //Log.w("Target.add", "b: " + b.toString()); 131 assert(a.mNumberOfBits == b.mNumberOfBits); 132 if (!a.mHasRange || !b.mHasRange) { 133 return newNan(a.mNumberOfBits); 134 } 135 return new Floaty(a.mNumberOfBits, new double[] { a.mValue + b.mValue, 136 a.mMinValue + b.mMinValue, 137 a.mMaxValue + b.mMaxValue }); 138 } 139 140 Floaty subtract(Floaty a, Floaty b) { 141 //Log.w("Target.subtract", "a: " + a.toString()); 142 //Log.w("Target.subtract", "b: " + b.toString()); 143 assert(a.mNumberOfBits == b.mNumberOfBits); 144 if (!a.mHasRange || !b.mHasRange) { 145 return newNan(a.mNumberOfBits); 146 } 147 return new Floaty(a.mNumberOfBits, new double[] { a.mValue - b.mValue, 148 a.mMinValue - b.mMaxValue, 149 a.mMaxValue - b.mMinValue }); 150 } 151 152 Floaty multiply(Floaty a, Floaty b) { 153 //Log.w("Target.multiply", "a: " + a.toString()); 154 //Log.w("Target.multiply", "b: " + b.toString()); 155 assert(a.mNumberOfBits == b.mNumberOfBits); 156 if (!a.mHasRange || !b.mHasRange) { 157 return newNan(a.mNumberOfBits); 158 } 159 return new Floaty(a.mNumberOfBits, new double[] { a.mValue * b.mValue, 160 a.mMinValue * b.mMinValue, 161 a.mMinValue * b.mMaxValue, 162 a.mMaxValue * b.mMinValue, 163 a.mMaxValue * b.mMaxValue}); 164 } 165 166 Floaty divide(Floaty a, Floaty b) { 167 //Log.w("Target.divide", "a: " + a.toString()); 168 //Log.w("Target.divide", "b: " + b.toString()); 169 assert(a.mNumberOfBits == b.mNumberOfBits); 170 if (!a.mHasRange || !b.mHasRange) { 171 return newNan(a.mNumberOfBits); 172 } 173 return new Floaty(a.mNumberOfBits, new double[] { a.mValue / b.mValue, 174 a.mMinValue / b.mMinValue, 175 a.mMinValue / b.mMaxValue, 176 a.mMaxValue / b.mMinValue, 177 a.mMaxValue / b.mMaxValue}); 178 } 179 180 /** Returns the absolute value of a Floaty. */ 181 Floaty abs(Floaty a) { 182 if (!a.mHasRange) { 183 return newNan(a.mNumberOfBits); 184 } 185 if (a.mMinValue >= 0 && a.mMaxValue >= 0) { 186 // Two non-negatives, no change 187 return a; 188 } 189 Floaty f = new Floaty(a); 190 f.mValue = Math.abs(a.mValue); 191 if (a.mMinValue < 0 && a.mMaxValue < 0) { 192 // Two negatives, we invert 193 f.mMinValue = -a.mMaxValue; 194 f.mMaxValue = -a.mMinValue; 195 } else { 196 // We have one negative, one positive. 197 f.mMinValue = 0.f; 198 f.mMaxValue = Math.max(-a.mMinValue, a.mMaxValue); 199 } 200 return f; 201 } 202 203 /** Returns the square root of a Floaty. */ 204 Floaty sqrt(Floaty a) { 205 //Log.w("Target.sqrt", "a: " + a.toString()); 206 if (!a.mHasRange) { 207 return newNan(a.mNumberOfBits); 208 } 209 double f = Math.sqrt(a.mValue); 210 double min = Math.sqrt(a.mMinValue); 211 double max = Math.sqrt(a.mMaxValue); 212 double[] values; 213 /* If the range of inputs covers 0, make sure we have it as one of 214 * the answers, to set the correct lowest bound, as the square root 215 * of the negative inputs will yield a NaN flag and won't affect the 216 * range. 217 */ 218 if (a.mMinValue < 0 && a.mMaxValue > 0) { 219 values = new double[]{f, 0., min, max}; 220 } else { 221 values = new double[]{f, min, max}; 222 } 223 Floaty answer = new Floaty(a.mNumberOfBits, values); 224 // Allow a little more imprecision for a square root operation. 225 answer.ExpandRangeByUlpFactor(); 226 return answer; 227 } 228 229 /** 230 * This class represents the range of floating point values we accept as the result of a 231 * computation performed by a runtime driver. 232 */ 233 class Floaty { 234 /** 235 * The number of bits the value should have, either 32 or 64. It would have been nice to 236 * use generics, e.g. Floaty<double> and Floaty<double> but Java does not support generics 237 * of float and double. Also, Java does not have an f16 type. This can simulate it, 238 * although more work will be needed. 239 */ 240 private int mNumberOfBits; 241 /** True if NaN is an acceptable value. */ 242 private boolean mCanBeNan; 243 /** 244 * True if mValue, mMinValue, mMaxValue have been set. This should be the case if mCanBeNan is false. 245 * It's possible for both mCanBeNan and mHasRange to be true at the same time. 246 */ 247 private boolean mHasRange; 248 /** 249 * The typical value we would expect. We don't just keep track of the min and max 250 * of the ranges of values allowed because some functions we are evaluating are 251 * discontinuous, e.g. sqrt around 0, lgamma around -1, -2, -3 and tan around pi/2. 252 * By keeping track of the middle value, we're more likely to handle this discontinuity 253 * correctly. 254 */ 255 private double mValue; 256 /** The minimum value we would expect. */ 257 private double mMinValue; 258 /** The maximum value we would expect. */ 259 private double mMaxValue; 260 261 Floaty(Floaty a) { 262 mNumberOfBits = a.mNumberOfBits; 263 mCanBeNan = a.mCanBeNan; 264 mHasRange = a.mHasRange; 265 mValue = a.mValue; 266 mMinValue = a.mMinValue; 267 mMaxValue = a.mMaxValue; 268 } 269 270 /** 271 * Creates a Floaty and initializes it so that the values passed could be represented by it. 272 * We also expand what's allowed by +/- mUlpFactor to allow for the various rounding modes. 273 * values[0] is treated as the representative case, otherwise the order of values does not matter. 274 */ 275 Floaty(int numberOfBits, double values[]) { 276 //Log.w("Floaty(double[], ulp)", "input: " + Arrays.toString(values) + ", ulp " + Integer.toString(mUlpFactor)); 277 mNumberOfBits = numberOfBits; 278 mCanBeNan = false; 279 mHasRange = false; 280 mValue = values[0]; 281 for (double f: values) { 282 if (f != f) { 283 mCanBeNan = true; 284 continue; 285 } 286 updateMinAndMax(f); 287 // For relaxed mode, we don't require support of subnormal values. 288 // If we have a subnormal value, we'll allow both the normalized value and zero, 289 // to cover the two ways this small value might be handled. 290 if (mIsRelaxedPrecision || mIsNative) { 291 if (IsSubnormal(f)) { 292 updateMinAndMax(0.f); 293 updateMinAndMax(smallestNormal(f)); 294 } 295 } 296 } 297 // Expand the range by one ulp factor to cover for the different rounding modes. 298 ExpandRangeByUlpFactor(); 299 //Log.w("Floaty(double[], ulp)", "output: " + toString()); 300 } 301 302 /** Modify the mMinValue and mMaxValue so that f is contained within the range. */ 303 private void updateMinAndMax(double f) { 304 if (mHasRange) { 305 if (f < mMinValue) { 306 mMinValue = f; 307 } 308 if (f > mMaxValue) { 309 mMaxValue = f; 310 } 311 } else { 312 mHasRange = true; 313 mMinValue = f; 314 mMaxValue = f; 315 } 316 } 317 318 /** Modify mMinValue and mMaxValue to allow one extra ulp factor of error on each side. */ 319 void ExpandRangeByUlpFactor() { 320 if (mHasRange && mUlpFactor > 0) { 321 // Expand the edges by the specified factor. 322 ExpandMin(mUlpFactor); 323 ExpandMax(mUlpFactor); 324 } 325 } 326 327 /** Expand the mMinValue by the number of ulp specified. */ 328 private void ExpandMin(int ulpFactor) { 329 //Log.w("ExpandMin", java.lang.Double.toString(mMinValue) + " by " + Integer.toString(ulpFactor)); 330 if (!mHasRange) { 331 return; 332 } 333 if (mMinValue == Double.NEGATIVE_INFINITY || 334 mMinValue == Double.POSITIVE_INFINITY) { 335 // Can't get any larger 336 //Log.w("ExpandMin", "infinity"); 337 return; 338 } 339 double ulp = NegativeUlp(); 340 double delta = ulp * ulpFactor; 341 double newValue = mMinValue + delta; 342 /* 343 * Reduce mMinValue but don't go negative if it's positive because the rounding error 344 * we're simulating won't change the sign. 345 */ 346 if (newValue < 0 && mMinValue > 0.f) { 347 mMinValue = 0.f; 348 } else { 349 mMinValue = newValue; 350 } 351 // If subnormal, also allow the normalized value if it's smaller. 352 if ((mIsRelaxedPrecision || mIsNative) && IsSubnormal(mMinValue)) { 353 if (mMinValue < 0) { 354 mMinValue = smallestNormal(-1.0f); 355 } else { 356 mMinValue = 0.f; 357 } 358 } 359 //Log.w("ExpandMin", "ulp " + java.lang.Double.toString(ulp) + ", delta " + java.lang.Double.toString(delta) + " for " + java.lang.Double.toString(mMinValue)); 360 } 361 362 /** Expand the mMaxValue by the number of ulp specified. */ 363 private void ExpandMax(int ulpFactor) { 364 //Log.w("ExpandMax", java.lang.Double.toString(mMaxValue) + " by " + Integer.toString(ulpFactor)); 365 if (!mHasRange) { 366 return; 367 } 368 if (mMaxValue == Double.NEGATIVE_INFINITY || 369 mMaxValue == Double.POSITIVE_INFINITY) { 370 // Can't get any larger 371 //Log.w("ExpandMax", "infinity"); 372 return; 373 } 374 double ulp = Ulp(); 375 double delta = ulp * ulpFactor; 376 double newValue = mMaxValue + delta; 377 /* 378 * Increase mMaxValue but don't go positive if it's negative because the rounding error 379 * we're simulating won't change the sign. 380 */ 381 if (newValue > 0 && mMaxValue < 0.f) { 382 mMaxValue = 0.f; 383 } else { 384 mMaxValue = newValue; 385 } 386 // If subnormal, also allow the normalized value if it's smaller. 387 if ((mIsRelaxedPrecision || mIsNative) && IsSubnormal(mMaxValue)) { 388 if (mMaxValue > 0) { 389 mMaxValue = smallestNormal(1.0f); 390 } else { 391 mMaxValue = 0.f; 392 } 393 } 394 //Log.w("ExpandMax", "ulp " + java.lang.Double.toString(ulp) + ", delta " + java.lang.Double.toString(delta) + " for " + java.lang.Double.toString(mMaxValue)); 395 } 396 397 /** 398 * Returns true if f is smaller than the smallest normalized number that can be represented 399 * by the number of bits we have. 400 */ 401 private boolean IsSubnormal(double f) { 402 double af = Math.abs(f); 403 return 0 < af && af < smallestNormal(1.0f); 404 } 405 406 /** 407 * Returns the smallest normal representable by the number of bits we have, of the same 408 * sign as f. 409 */ 410 private double smallestNormal(double f) { 411 double answer = (mNumberOfBits == 32) ? Float.MIN_NORMAL : Double.MIN_NORMAL; 412 if (f < 0) { 413 answer = -answer; 414 } 415 return answer; 416 } 417 418 /** Returns the unit of least precision for the maximum value allowed. */ 419 private double Ulp() { 420 double u; 421 if (mNumberOfBits == 32) { 422 u = Math.ulp((float)mMaxValue); 423 } else { 424 u = Math.ulp(mMaxValue); 425 } 426 if (mIsRelaxedPrecision || mIsNative) { 427 u = Math.max(u, smallestNormal(1.f)); 428 } 429 return u; 430 } 431 432 /** Returns the negative of the unit of least precision for the minimum value allowed. */ 433 private double NegativeUlp() { 434 double u; 435 if (mNumberOfBits == 32) { 436 u = -Math.ulp((float)mMinValue); 437 } else { 438 u = -Math.ulp(mMinValue); 439 } 440 if (mIsRelaxedPrecision || mIsNative) { 441 u = Math.min(u, smallestNormal(-1.f)); 442 } 443 return u; 444 } 445 446 /** Returns true if the number passed is among the values that are represented by this Floaty. */ 447 public boolean couldBe(double a) { 448 return couldBe(a, 0.0); 449 } 450 451 /** 452 * Returns true if the number passed is among the values that are represented by this Floaty. 453 * An extra amount of allowed error can be specified. 454 */ 455 public boolean couldBe(double a, double extraAllowedError) { 456 //Log.w("Floaty.couldBe", "Can " + Double.toString(a) + " be " + toString() + "? "); 457 // Handle the input being a NaN. 458 if (a != a) { 459 //Log.w("couldBe", "true because is Naan"); 460 return mCanBeNan; 461 } 462 // If the input is not a NaN, we better have a range. 463 if (!mHasRange) { 464 return false; 465 } 466 // Handle the simple case. 467 if ((mMinValue - extraAllowedError) <= a && a <= (mMaxValue + extraAllowedError)) { 468 return true; 469 } 470 // For native, we don't require returning +/- infinity. If that's what we expect, 471 // allow all answers. 472 if (mIsNative) { 473 if (mMinValue == Double.NEGATIVE_INFINITY && 474 mMaxValue == Double.NEGATIVE_INFINITY) { 475 return true; 476 } 477 if (mMinValue == Double.POSITIVE_INFINITY && 478 mMaxValue == Double.POSITIVE_INFINITY) { 479 return true; 480 } 481 } 482 return false; 483 } 484 485 486 public double get64() { return mValue; } 487 public double min64() { return mMinValue; } 488 public double max64() { return mMaxValue; } 489 /** 490 * Returns mValue unless zero could be a legal value. In that case, return it. 491 * This is useful for testing functions where the behavior at zero is unusual, 492 * e.g. reciprocals. If mValue is already +0.0 or -0.0, don't change it, to 493 * preserve the sign. 494 */ 495 public double mid64() { 496 if (mMinValue < 0.0 && mMaxValue > 0.0 && mValue != 0.0) { 497 return 0.0; 498 } 499 return mValue; 500 } 501 502 public float get32() { return (float) mValue; } 503 public float min32() { return (float) mMinValue; } 504 public float max32() { return (float) mMaxValue; } 505 public float mid32() { return (float) mid64(); } 506 507 public String toString() { 508 String s = String.format("[f%d: ", mNumberOfBits); 509 if (mCanBeNan) { 510 s += "NaN, "; 511 } 512 if (mHasRange) { 513 if (mNumberOfBits == 32) { 514 float min = (float)mMinValue; 515 float mid = (float)mValue; 516 float max = (float)mMaxValue; 517 s += String.format("%11.9g {%08x} to %11.9g {%08x} to %11.9g {%08x}", min, 518 Float.floatToRawIntBits(min), mid, 519 Float.floatToRawIntBits(mid), max, 520 Float.floatToRawIntBits(max)); 521 } else { 522 s += String.format("%24.9g {%16x} to %24.9g {%16x} to %24.9g {%16x}", mMinValue, 523 Double.doubleToRawLongBits(mMinValue), mValue, 524 Double.doubleToRawLongBits(mValue), mMaxValue, 525 Double.doubleToRawLongBits(mMaxValue)); 526 } 527 } 528 s += "]"; 529 return s; 530 } 531 } 532 } 533