1 /* 2 * Copyright (C) 2016 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.renderscript.cts.Target; 20 import android.renderscript.cts.Float16Utils; 21 import android.renderscript.RSRuntimeException; 22 import android.util.Log; 23 24 public class FloatyUnitTest extends RSBaseCompute { 25 static float subnormalFloat = 10000 * Float.MIN_VALUE; 26 static float normalFloat1 = 1.7833920e+16f; 27 static float normalFloat2 = -1.9905756e-16f; 28 29 static double subnormalDouble = 10000 * Double.MIN_VALUE; 30 static double normalDouble = 1.7833920e+16; 31 32 static double normalHalf = 1985; // 2048 - 63. Magic number chosen for use in testHalf128Ulp 33 static double negativeNormalHalf = -237; 34 35 // Some double values that are precisely representable in half-precision. 36 static double[] preciseFloat16Values = {Double.NaN, 37 Double.POSITIVE_INFINITY, 38 Double.NEGATIVE_INFINITY, 39 0., 1, 2, 74.0625, 3000, 65504, 40 Float16Utils.MIN_VALUE, 41 Float16Utils.MIN_VALUE * 100, 42 -0., -1, -2, -74.0625, -3000, -65504, 43 -Float16Utils.MIN_VALUE, 44 -Float16Utils.MIN_VALUE * 100, 45 }; 46 47 // Fail if Floaty f with an extra error allowance of 'extraAllowedError' doesn't accept 'value' shouldAccept(Target.Floaty f, double value, double extraAllowedError)48 private void shouldAccept(Target.Floaty f, double value, double extraAllowedError) { 49 if (!f.couldBe(value, extraAllowedError)) { 50 StringBuilder message = new StringBuilder(); 51 message.append("Floaty: "); 52 appendVariableToMessage(message, f); 53 message.append("\n"); 54 if (extraAllowedError > 0.) { 55 message.append("extraAllowedError: "); 56 appendVariableToMessage(message, extraAllowedError); 57 message.append("\n"); 58 } 59 message.append("Value: "); 60 appendVariableToMessage(message, (float) value); 61 message.append("\n"); 62 assertTrue("Floaty incorrectly doesn't accept value:\n" + message.toString(), false); 63 } 64 } 65 66 // Fail if Floaty f doesn't accept value shouldAccept(Target.Floaty f, double value)67 private void shouldAccept(Target.Floaty f, double value) { 68 shouldAccept(f, value, 0.); 69 } 70 71 // Fail if Floaty f with an extra error allowance of 'extraAllowedError' accepts 'value' shouldNotAccept(Target.Floaty f, double value, double extraAllowedError)72 private void shouldNotAccept(Target.Floaty f, double value, double extraAllowedError) { 73 if (f.couldBe(value)) { 74 StringBuilder message = new StringBuilder(); 75 message.append("Floaty: "); 76 appendVariableToMessage(message, f); 77 message.append("\n"); 78 if (extraAllowedError > 0.) { 79 message.append("extraAllowedError: "); 80 appendVariableToMessage(message, extraAllowedError); 81 message.append("\n"); 82 } 83 message.append("Value: "); 84 appendVariableToMessage(message, (float) value); 85 message.append("\n"); 86 assertTrue("Floaty incorrectly accepts value:\n" + message.toString(), false); 87 } 88 } 89 90 // Fail if Floaty f accepts value shouldNotAccept(Target.Floaty f, double value)91 private void shouldNotAccept(Target.Floaty f, double value) { 92 shouldNotAccept(f, value, 0.); 93 } 94 95 // Test Target that accepts precise 1ulp error for floating values. testFloat1Ulp()96 public void testFloat1Ulp() { 97 Target t = new Target(Target.FunctionType.NORMAL, Target.ReturnType.FLOAT, false); 98 t.setPrecision(1, 1); 99 100 Target.Floaty subnormalFloaty = t.new32(subnormalFloat); 101 Target.Floaty normalFloaty = t.new32(normalFloat1); 102 103 // for subnormal 104 shouldAccept(subnormalFloaty, (double) subnormalFloat); 105 shouldAccept(subnormalFloaty, (double) subnormalFloat + Math.ulp(subnormalFloat)); 106 shouldAccept(subnormalFloaty, (double) subnormalFloat - Math.ulp(subnormalFloat)); 107 shouldNotAccept(subnormalFloaty, (double) subnormalFloat + 2 * Math.ulp(subnormalFloat)); 108 shouldNotAccept(subnormalFloaty, (double) subnormalFloat - 2 * Math.ulp(subnormalFloat)); 109 shouldNotAccept(subnormalFloaty, (double) normalFloat1); 110 111 // for normalFloaty 112 shouldAccept(normalFloaty, (double) normalFloat1); 113 shouldAccept(normalFloaty, (double) normalFloat1 + Math.ulp(normalFloat1)); 114 shouldAccept(normalFloaty, (double) normalFloat1 - Math.ulp(normalFloat1)); 115 shouldNotAccept(normalFloaty, (double) normalFloat1 + 2 * Math.ulp(normalFloat1)); 116 shouldNotAccept(normalFloaty, (double) normalFloat1 - 2 * Math.ulp(normalFloat1)); 117 shouldNotAccept(normalFloaty, (double) subnormalFloat); 118 } 119 120 // Test Target that accepts precise 8192ulp error for floating values. testFloat8192Ulp()121 public void testFloat8192Ulp() { 122 Target t = new Target(Target.FunctionType.NORMAL, Target.ReturnType.FLOAT, false); 123 t.setPrecision(8192, 8192); 124 125 Target.Floaty subnormalFloaty = t.new32(subnormalFloat); 126 Target.Floaty normalFloaty = t.new32(normalFloat2); 127 128 // for subnormalFloaty 129 shouldAccept(subnormalFloaty, (double) subnormalFloat); 130 shouldAccept(subnormalFloaty, (double) subnormalFloat + 8192 * Math.ulp(subnormalFloat)); 131 shouldAccept(subnormalFloaty, (double) subnormalFloat - 8192 * Math.ulp(subnormalFloat)); 132 shouldNotAccept(subnormalFloaty, (double) subnormalFloat + 8193 * Math.ulp(subnormalFloat)); 133 shouldNotAccept(subnormalFloaty, (double) subnormalFloat - 8193 * Math.ulp(subnormalFloat)); 134 shouldNotAccept(subnormalFloaty, (double) normalFloat1); 135 136 // for normalFloaty 137 shouldAccept(normalFloaty, (double) normalFloat2); 138 shouldAccept(normalFloaty, (double) normalFloat2 + 8192 * Math.ulp(normalFloat2)); 139 shouldAccept(normalFloaty, (double) normalFloat2 - 8192 * Math.ulp(normalFloat2)); 140 shouldNotAccept(normalFloaty, (double) normalFloat2 + 8193 * Math.ulp(normalFloat2)); 141 shouldNotAccept(normalFloaty, (double) normalFloat2 - 8193 * Math.ulp(normalFloat2)); 142 shouldNotAccept(normalFloaty, (double) subnormalFloat); 143 } 144 145 // Test Target that accepts relaxed 1ulp error for floating values. testFloat1UlpRelaxed()146 public void testFloat1UlpRelaxed() { 147 Target t = new Target(Target.FunctionType.NORMAL, Target.ReturnType.FLOAT, true); 148 t.setPrecision(1, 1); 149 150 Target.Floaty subnormalFloaty = t.new32(subnormalFloat); 151 152 // for subnormal 153 shouldAccept(subnormalFloaty, (double) subnormalFloat); 154 // In relaxed mode, Floaty uses the smallest normal as the ULP if ULP is subnormal. 155 shouldAccept(subnormalFloaty, (double) Float.MIN_NORMAL + Float.MIN_NORMAL); 156 shouldAccept(subnormalFloaty, (double) 0.f - Float.MIN_NORMAL); 157 shouldNotAccept(subnormalFloaty, (double) Float.MIN_NORMAL + 2 * Float.MIN_NORMAL); 158 shouldNotAccept(subnormalFloaty, (double) 0.f - 2 * Float.MIN_NORMAL); 159 shouldNotAccept(subnormalFloaty, (double) normalFloat1); 160 } 161 162 // Test Target that accepts relaxed 8192ulp error for floating values. testFloat8192UlpRelaxed()163 public void testFloat8192UlpRelaxed() { 164 Target t = new Target(Target.FunctionType.NORMAL, Target.ReturnType.FLOAT, true); 165 t.setPrecision(8192, 8192); 166 167 Target.Floaty subnormalFloaty = t.new32(subnormalFloat); 168 169 // for subnormalFloaty 170 shouldAccept(subnormalFloaty, (double) subnormalFloat); 171 // In relaxed mode, Floaty uses the smallest normal as the ULP if ULP is subnormal. 172 shouldAccept(subnormalFloaty, (double) Float.MIN_NORMAL + 8192 * Float.MIN_NORMAL); 173 shouldAccept(subnormalFloaty, (double) 0.f - 8192 * Float.MIN_NORMAL); 174 shouldNotAccept(subnormalFloaty, (double) Float.MIN_NORMAL + 8193 * Float.MIN_NORMAL); 175 shouldNotAccept(subnormalFloaty, (double) 0.f - 8193 * Float.MIN_NORMAL); 176 shouldNotAccept(subnormalFloaty, (double) normalFloat1); 177 } 178 179 // Test Target that accepts precise 1ulp error for double values. testDouble1Ulp()180 public void testDouble1Ulp() { 181 Target t = new Target(Target.FunctionType.NORMAL, Target.ReturnType.DOUBLE, false); 182 t.setPrecision(1, 1); 183 184 Target.Floaty subnormalFloaty = t.new64(subnormalDouble); 185 Target.Floaty normalFloaty = t.new64(normalDouble); 186 187 // for subnormal 188 shouldAccept(subnormalFloaty, subnormalDouble); 189 shouldAccept(subnormalFloaty, subnormalDouble + Math.ulp(subnormalDouble)); 190 shouldAccept(subnormalFloaty, subnormalDouble - Math.ulp(subnormalDouble)); 191 shouldNotAccept(subnormalFloaty, subnormalDouble + 2 * Math.ulp(subnormalDouble)); 192 shouldNotAccept(subnormalFloaty, subnormalDouble - 2 * Math.ulp(subnormalDouble)); 193 shouldNotAccept(subnormalFloaty, normalDouble); 194 195 // for normalFloaty 196 shouldAccept(normalFloaty, normalDouble); 197 shouldAccept(normalFloaty, normalDouble + Math.ulp(normalDouble)); 198 shouldAccept(normalFloaty, normalDouble - Math.ulp(normalDouble)); 199 shouldNotAccept(normalFloaty, normalDouble + 2 * Math.ulp(normalDouble)); 200 shouldNotAccept(normalFloaty, normalDouble - 2 * Math.ulp(normalDouble)); 201 shouldNotAccept(normalFloaty, subnormalDouble); 202 } 203 204 // Test Target that accepts precise 8192ulp error for double values. testDouble8192Ulp()205 public void testDouble8192Ulp() { 206 Target t = new Target(Target.FunctionType.NORMAL, Target.ReturnType.DOUBLE, false); 207 t.setPrecision(8192, 8192); 208 209 Target.Floaty subnormalFloaty = t.new64(subnormalDouble); 210 Target.Floaty normalFloaty = t.new64(normalDouble); 211 212 // for subnormal 213 shouldAccept(subnormalFloaty, subnormalDouble); 214 shouldAccept(subnormalFloaty, subnormalDouble + 8192 * Math.ulp(subnormalDouble)); 215 shouldAccept(subnormalFloaty, subnormalDouble - 8192 * Math.ulp(subnormalDouble)); 216 shouldNotAccept(subnormalFloaty, subnormalDouble + 8193 * Math.ulp(subnormalDouble)); 217 shouldNotAccept(subnormalFloaty, subnormalDouble - 8193 * Math.ulp(subnormalDouble)); 218 shouldNotAccept(subnormalFloaty, normalDouble); 219 220 // for normalFloaty 221 shouldAccept(normalFloaty, normalDouble); 222 shouldAccept(normalFloaty, normalDouble + 8192 * Math.ulp(normalDouble)); 223 shouldAccept(normalFloaty, normalDouble - 8192 * Math.ulp(normalDouble)); 224 shouldNotAccept(normalFloaty, normalDouble + 8193 * Math.ulp(normalDouble)); 225 shouldNotAccept(normalFloaty, normalDouble - 8193 * Math.ulp(normalDouble)); 226 shouldNotAccept(normalFloaty, subnormalDouble); 227 } 228 229 // Test Target that accepts precise 1ulp error for half values. testHalf1Ulp()230 public void testHalf1Ulp() { 231 Target t = new Target(Target.FunctionType.NORMAL, Target.ReturnType.HALF, false); 232 t.setPrecision(1, 1); 233 234 Target.Floaty normalFloaty = t.newFloaty(normalHalf); 235 shouldAccept(normalFloaty, normalHalf); 236 shouldAccept(normalFloaty, normalHalf + Float16Utils.float16Ulp(normalHalf)); 237 shouldAccept(normalFloaty, normalHalf - Float16Utils.float16Ulp(normalHalf)); 238 shouldNotAccept(normalFloaty, normalHalf + 2 * Float16Utils.float16Ulp(normalHalf)); 239 shouldNotAccept(normalFloaty, normalHalf - 2 * Float16Utils.float16Ulp(normalHalf)); 240 shouldNotAccept(normalFloaty, 2500); 241 } 242 243 // Test Target that accepts precise 128ulp error for half values. testHalf128Ulp()244 public void testHalf128Ulp() { 245 Target t = new Target(Target.FunctionType.NORMAL, Target.ReturnType.HALF, false); 246 t.setPrecision(128, 128); 247 248 Target.Floaty normalFloaty = t.newFloaty(normalHalf); 249 shouldAccept(normalFloaty, normalHalf); 250 shouldAccept(normalFloaty, normalHalf + 128 * Float16Utils.float16Ulp(normalHalf)); 251 shouldAccept(normalFloaty, normalHalf - 128 * Float16Utils.float16Ulp(normalHalf)); 252 shouldNotAccept(normalFloaty, normalHalf - 129 * Float16Utils.float16Ulp(normalHalf)); 253 254 // Since normalHalf + 128ULP has a higher exponent, Floaty accepts an extra ULP to maintin 255 // the invariant that the range is always precisely representable in Float16. 256 shouldAccept(normalFloaty, normalHalf + 129 * Float16Utils.float16Ulp(normalHalf)); 257 shouldNotAccept(normalFloaty, normalHalf + 130 * Float16Utils.float16Ulp(normalHalf)); 258 shouldNotAccept(normalFloaty, 2500); 259 } 260 testExtraAllowedError()261 public void testExtraAllowedError() { 262 Target t; 263 double extraError, lb, ub; 264 265 t = new Target(Target.FunctionType.NORMAL, Target.ReturnType.FLOAT, false); 266 t.setPrecision(4, 4); 267 268 // Test normal float value with extraAllowedError 269 extraError = 1e-15; 270 Target.Floaty normalFloaty = t.newFloaty(normalFloat2); 271 ub = normalFloat2 + 4 * Math.ulp(normalFloat2) + extraError; 272 lb = normalFloat2 - 4 * Math.ulp(normalFloat2) - extraError; 273 shouldAccept(normalFloaty, ub, extraError); 274 shouldAccept(normalFloaty, lb, extraError); 275 shouldNotAccept(normalFloaty, ub + Math.ulp(ub), extraError); 276 shouldNotAccept(normalFloaty, lb - Math.ulp(lb), extraError); 277 278 t = new Target(Target.FunctionType.NORMAL, Target.ReturnType.FLOAT, false); 279 t.setPrecision(2, 2); 280 extraError = Float.MIN_VALUE; 281 282 // Test subnormal float value with extraAllowedError 283 Target.Floaty subnormalFloaty = t.newFloaty(subnormalFloat); 284 ub = subnormalFloat + 2 * Math.ulp(subnormalFloat) + extraError; 285 lb = subnormalFloat - 2 * Math.ulp(subnormalFloat) - extraError; 286 shouldAccept(subnormalFloaty, ub, extraError); 287 shouldAccept(subnormalFloaty, lb, extraError); 288 shouldNotAccept(subnormalFloaty, ub + Math.ulp(ub), extraError); 289 shouldNotAccept(subnormalFloaty, lb - Math.ulp(lb), extraError); 290 291 t = new Target(Target.FunctionType.NATIVE, Target.ReturnType.HALF, false); 292 t.setPrecision(0, 0); 293 294 // Test Float16 with extraAllowedError in same order of magnitude 295 extraError = 2.0; 296 Target.Floaty halfFloaty = t.newFloaty(normalHalf); 297 ub = normalHalf + extraError; 298 lb = normalHalf - extraError; 299 shouldAccept(halfFloaty, ub, extraError); 300 shouldAccept(halfFloaty, lb, extraError); 301 shouldNotAccept(halfFloaty, ub + Float16Utils.float16Ulp(ub), extraError); 302 shouldNotAccept(halfFloaty, lb - Float16Utils.float16Ulp(lb), extraError); 303 304 // Test Float16 with a tiny extraAllowedError 305 extraError = Float16Utils.MIN_NORMAL; 306 ub = normalHalf; 307 lb = normalHalf; 308 shouldAccept(halfFloaty, ub, extraError); 309 shouldAccept(halfFloaty, lb, extraError); 310 shouldNotAccept(halfFloaty, ub + Float16Utils.float16Ulp(ub), extraError); 311 shouldNotAccept(halfFloaty, lb - Float16Utils.float16Ulp(lb), extraError); 312 313 // Test negative Float16 with extraAllowedError in same order of magnitude 314 extraError = 2.0; 315 Target.Floaty negativeHalfFloaty = t.newFloaty(negativeNormalHalf); 316 ub = negativeNormalHalf + extraError; 317 lb = negativeNormalHalf - extraError; 318 shouldAccept(negativeHalfFloaty, ub, extraError); 319 shouldAccept(negativeHalfFloaty, lb, extraError); 320 shouldNotAccept(negativeHalfFloaty, ub + Float16Utils.float16Ulp(ub), extraError); 321 shouldNotAccept(negativeHalfFloaty, lb - Float16Utils.float16Ulp(lb), extraError); 322 323 // Test negative Float16 with a tiny extraAllowedError 324 extraError = Float16Utils.MIN_NORMAL; 325 ub = negativeNormalHalf; 326 lb = negativeNormalHalf; 327 shouldAccept(negativeHalfFloaty, ub, extraError); 328 shouldAccept(negativeHalfFloaty, lb, extraError); 329 shouldNotAccept(negativeHalfFloaty, ub + Float16Utils.float16Ulp(ub), extraError); 330 shouldNotAccept(negativeHalfFloaty, lb - Float16Utils.float16Ulp(lb), extraError); 331 } 332 333 // Test that range of allowed error is trimmed at the zero boundary. This function tests both 334 // float, double, and half Targets. testRangeDoesNotAcrossZero()335 public void testRangeDoesNotAcrossZero() { 336 Target t; 337 Target.Floaty floaty; 338 339 t = new Target(Target.FunctionType.NORMAL, Target.ReturnType.FLOAT, false); 340 t.setPrecision(4, 4); 341 342 floaty = t.new32(Float.MIN_VALUE); 343 shouldAccept(floaty, (double) Float.MIN_VALUE); 344 shouldAccept(floaty, (double) Float.MIN_VALUE + 4 * Float.MIN_VALUE); 345 shouldAccept(floaty, (double) 0.f); 346 shouldNotAccept(floaty, (double) 0.f - Float.MIN_VALUE); 347 348 floaty = t.new32(-Float.MIN_VALUE); 349 shouldAccept(floaty, (double) -Float.MIN_VALUE); 350 shouldAccept(floaty, (double) -Float.MIN_VALUE - 4 * Float.MIN_VALUE); 351 shouldAccept(floaty, (double) 0.f); 352 shouldNotAccept(floaty, (double) 0.f + Float.MIN_VALUE); 353 354 t = new Target(Target.FunctionType.NORMAL, Target.ReturnType.DOUBLE, false); 355 t.setPrecision(4, 4); 356 357 floaty = t.new64(Double.MIN_VALUE); 358 shouldAccept(floaty, Double.MIN_VALUE); 359 shouldAccept(floaty, Double.MIN_VALUE + 4 * Double.MIN_VALUE); 360 shouldAccept(floaty, 0.); 361 shouldNotAccept(floaty, 0. - Double.MIN_VALUE); 362 363 floaty = t.new64(-Double.MIN_VALUE); 364 shouldAccept(floaty, -Double.MIN_VALUE); 365 shouldAccept(floaty, -Double.MIN_VALUE - 4 * Double.MIN_VALUE); 366 shouldAccept(floaty, 0.); 367 shouldNotAccept(floaty, 0. + Double.MIN_VALUE); 368 369 t = new Target(Target.FunctionType.NORMAL, Target.ReturnType.HALF, false); 370 t.setPrecision(4, 4); 371 372 // Subnormals are not required to be handled for Float16. Test with MIN_NORMAL instead. 373 floaty = t.newFloaty(Float16Utils.MIN_NORMAL); 374 shouldAccept(floaty, Float16Utils.MIN_NORMAL); 375 shouldAccept(floaty, Float16Utils.MIN_NORMAL+ 4 * Double.MIN_NORMAL); 376 shouldAccept(floaty, 0.); 377 shouldNotAccept(floaty, 0. - Float16Utils.MIN_NORMAL); 378 379 floaty = t.newFloaty(-Float16Utils.MIN_NORMAL); 380 shouldAccept(floaty, -Float16Utils.MIN_NORMAL); 381 shouldAccept(floaty, -Float16Utils.MIN_NORMAL- 4 * Float16Utils.MIN_NORMAL); 382 shouldAccept(floaty, 0.f); 383 shouldNotAccept(floaty, 0. + Float16Utils.MIN_NORMAL); 384 } 385 386 // Validate float16Ulp for a double value that is precisely representable in half-precision. validateFloat16Ulp(double value)387 private void validateFloat16Ulp(double value) { 388 double absoluteValue = Math.abs(value); 389 double ulp = Float16Utils.float16Ulp(value); 390 391 if (absoluteValue == 0.) { 392 assertEquals(Float16Utils.MIN_VALUE, ulp); 393 return; 394 } 395 if (Double.isNaN(absoluteValue)) { 396 assertTrue("Ulp for NaN is not NaN", Double.isNaN(ulp)); 397 return; 398 } 399 if (absoluteValue == Double.POSITIVE_INFINITY || 400 absoluteValue == Double.NEGATIVE_INFINITY) { 401 assertEquals(Double.POSITIVE_INFINITY, ulp); 402 return; 403 } 404 405 if (absoluteValue < Math.scalb(1., -24)) { 406 assertTrue("Input double value smaller than MIN_VALUE for Float16", false); 407 } 408 409 if (absoluteValue < Math.scalb(1., -14)) { 410 // Input is subnormal Float16. Ulp should be 2^-24 411 assertEquals(Math.scalb(1., -24), ulp); 412 return; 413 } 414 415 boolean tested = false; 416 int exponent = -13; 417 double limit = Math.scalb(1., exponent); 418 for (; exponent <= 16; exponent ++, limit *= 2) { 419 if (absoluteValue < limit) { 420 assertEquals(ulp, Math.scalb(1., exponent - 11)); 421 tested = true; 422 break; 423 } 424 } 425 assertTrue("Ulp not validated. Absolute value " + Double.toString(absoluteValue), tested); 426 } 427 428 // Test float16Ulp for all valid inputs (i.e. all Float16 values represented as Double) and test 429 // that assertions fire for Double values that are not representable in Float16. testFloat16Ulp()430 public void testFloat16Ulp() { 431 // Test float16Ulp for all short values. 432 for (short s = Short.MIN_VALUE; ; s ++) { 433 validateFloat16Ulp(Float16Utils.convertFloat16ToDouble(s)); 434 // Detect loop termination here. Doing so in the for statement creates an infinite 435 // loop. 436 if (s == Short.MAX_VALUE) { 437 break; 438 } 439 } 440 441 // Test float16Ulp for some known values 442 for (double value: preciseFloat16Values) { 443 validateFloat16Ulp(value); 444 } 445 446 // Expect an exception for values not representable in Float16. The loop below tests this 447 // for all values in this array and their negation. 448 double valuesOutOfFloat16Range[] = {Math.scalb(1., -100), 449 Double.valueOf("0x1.8p-24"), 450 Double.valueOf("0x1.ffffffffp-20"), 451 1024.55, 65520., 66000. 452 }; 453 454 for (double value: valuesOutOfFloat16Range) { 455 try { 456 Float16Utils.float16Ulp(value); 457 assertTrue("Expected exception not thrown for: " + Double.toString(value), false); 458 } catch (RSRuntimeException e) { 459 // Ignore the expected exception. 460 // Log.w("testFloat16Ulp", "Received expected exception: " + e.getMessage()); 461 } 462 463 try { 464 Float16Utils.float16Ulp(-value); 465 assertTrue("Expected exception not thrown for: " + Double.toString(value), false); 466 } catch (RSRuntimeException e) { 467 // Ignore the expected exception. 468 // Log.w("testFloat16Ulp", "Received expected exception: " + e.getMessage()); 469 } 470 } 471 } 472 validateRoundToFloat16(double argument, double low, double high)473 private void validateRoundToFloat16(double argument, double low, double high) { 474 double[] result = Float16Utils.roundToFloat16(argument); 475 double[] expected = new double[]{low, high}; 476 477 for (int i = 0; i < 2; i ++) { 478 if (result[i] == expected[i]) 479 continue; 480 if (Double.isNaN(result[i]) && Double.isNaN(expected[i])) 481 continue; 482 483 StringBuilder message = new StringBuilder(); 484 message.append("For input "); 485 appendVariableToMessage(message, argument); 486 message.append("\n"); 487 message.append("For field " + Integer.toString(i) + ": Expected output: "); 488 appendVariableToMessage(message, expected[i]); 489 message.append("\n"); 490 message.append("Actual output: "); 491 appendVariableToMessage(message, result[i]); 492 message.append("\n"); 493 assertTrue("roundToFloat16 error:\n" + message.toString(), false); 494 } 495 } 496 testRoundToFloat16()497 public void testRoundToFloat16() { 498 // Validate values that are precisely representable in Float16. The bounds have to be the 499 // same as the input. 500 for (double value: preciseFloat16Values) { 501 validateRoundToFloat16(value, value, value); 502 } 503 504 // Validate for special cases: 505 // Double.MIN_VALUE is between Float16 0 and MIN_VALUE 506 // Double.MAX_VALUE is between Float16 MAX_VALUE and +Infinity 507 // 65519 and 65520 are between Float16 MAX_VALUE and +Infinity (but their exponent is 508 // within Float16's range). 509 // 3001.5 and 3000.5 are between Float16 3000 and 3002 510 validateRoundToFloat16(Double.MIN_VALUE, 0.0, Float16Utils.MIN_VALUE); 511 validateRoundToFloat16(Double.MAX_VALUE, Double.POSITIVE_INFINITY, 512 Double.POSITIVE_INFINITY); 513 validateRoundToFloat16(65519., 65504., Double.POSITIVE_INFINITY); 514 validateRoundToFloat16(65520., 65504., Double.POSITIVE_INFINITY); 515 validateRoundToFloat16(3001.5, 3000., 3002.); 516 validateRoundToFloat16(3000.5, 3000., 3002.); 517 518 validateRoundToFloat16(-Double.MIN_VALUE, -Float16Utils.MIN_VALUE, -0.0); 519 validateRoundToFloat16(-Double.MAX_VALUE, Double.NEGATIVE_INFINITY, 520 Double.NEGATIVE_INFINITY); 521 validateRoundToFloat16(-65519., Double.NEGATIVE_INFINITY, -65504.); 522 validateRoundToFloat16(-65520., Double.NEGATIVE_INFINITY, -65504.); 523 validateRoundToFloat16(-3001.5, -3002., -3000.); 524 validateRoundToFloat16(-3000.5, -3002., -3000.); 525 526 // Validate that values precisely between two Float16 values get handled properly. 527 double[] float16Sample = {0., 1., 100., 528 Float16Utils.MIN_VALUE, 529 Float16Utils.MIN_VALUE * 100, 530 Float16Utils.MIN_NORMAL * 100 531 }; 532 533 for (double value: float16Sample) { 534 double ulp = Float16Utils.float16Ulp(value); 535 536 validateRoundToFloat16(value + 0.5 * ulp, value, value + ulp); 537 validateRoundToFloat16(-value - 0.5 * ulp, -value - ulp, -value); 538 } 539 } 540 testConvertDoubleToFloat16()541 public void testConvertDoubleToFloat16() { 542 assertEquals(Float16Utils.convertDoubleToFloat16(0.), (short) 0x0); 543 assertEquals(Float16Utils.convertDoubleToFloat16(Float16Utils.MIN_VALUE), (short) 0x0001); 544 assertEquals(Float16Utils.convertDoubleToFloat16(42 * Float16Utils.MIN_VALUE), (short) 0x002a); 545 assertEquals(Float16Utils.convertDoubleToFloat16(Float16Utils.MIN_NORMAL), (short) 0x400); 546 assertEquals(Float16Utils.convertDoubleToFloat16(42.), (short) 0x5140); 547 assertEquals(Float16Utils.convertDoubleToFloat16(1024.), (short) 0x6400); 548 assertEquals(Float16Utils.convertDoubleToFloat16(Double.POSITIVE_INFINITY), (short) 0x7c00); 549 550 assertEquals(Float16Utils.convertDoubleToFloat16(-0.), (short) 0x8000); 551 assertEquals(Float16Utils.convertDoubleToFloat16(-Float16Utils.MIN_VALUE), (short) 0x8001); 552 assertEquals(Float16Utils.convertDoubleToFloat16(-42 * Float16Utils.MIN_VALUE), (short) 0x802a); 553 assertEquals(Float16Utils.convertDoubleToFloat16(-Float16Utils.MIN_NORMAL), (short) 0x8400); 554 assertEquals(Float16Utils.convertDoubleToFloat16(-42.), (short) 0xd140); 555 assertEquals(Float16Utils.convertDoubleToFloat16(-1024.), (short) 0xe400); 556 assertEquals(Float16Utils.convertDoubleToFloat16(Double.NEGATIVE_INFINITY), (short) 0xfc00); 557 558 assertEquals(Float16Utils.convertDoubleToFloat16(Double.NaN), (short) 0x7e00); 559 } 560 } 561