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 * Broad classification of the type of function being tested. 32 */ 33 public enum FunctionType { 34 NORMAL, 35 HALF, 36 NATIVE, 37 FAST, 38 } 39 40 /** 41 * Floating point precision of the result of the function being tested. 42 */ 43 public enum ReturnType { 44 HALF, 45 FLOAT, 46 DOUBLE 47 } 48 49 /* The classification of the function being tested */ 50 private FunctionType mFunctionType; 51 52 /* The floating point precision of the result of the function being tested */ 53 private ReturnType mReturnType; 54 55 /* 56 * In relaxed precision mode, we allow: 57 * - less precision in the computation 58 * - using only normalized values 59 * - reordering of the operations 60 * - different rounding mode 61 */ 62 private boolean mIsRelaxedPrecision; 63 64 /* If false, zero or the closest normal value are also accepted for subnormal results. */ 65 private boolean mHandleSubnormal; 66 67 /* Number of bits in mReturnType */ 68 private int mFloatSize; 69 70 /** 71 * How much we'll allow the values tested to diverge from the values 72 * we compute. This can be very large for native_* and half_* tests. 73 */ 74 private int mUlpFactor; 75 Target(FunctionType functionType, ReturnType returnType, boolean relaxed)76 Target(FunctionType functionType, ReturnType returnType, boolean relaxed) { 77 mFunctionType = functionType; 78 mReturnType = returnType; 79 mIsRelaxedPrecision = relaxed; 80 81 if (mReturnType == ReturnType.HALF) { 82 // HALF accepts relaxed precision by default - subnormal numbers need not be handled. 83 // 'relaxed' parameter is ignored. 84 mHandleSubnormal = false; 85 mFloatSize = 16; 86 } else if (mReturnType == ReturnType.FLOAT) { 87 // NORMAL functions, when in non-relaxed mode, need to handle subnormals. Subnormals 88 // need not be handled in any other mode. 89 mHandleSubnormal = (mFunctionType == FunctionType.NORMAL) && !relaxed; 90 mFloatSize = 32; 91 } else if (mReturnType == ReturnType.DOUBLE) { 92 // We only have NORMAL functions for DOUBLE, with no relaxed precison - subnormal values 93 // must always be handled. 'relaxed' parameter is ignored. 94 assert(mFunctionType == FunctionType.NORMAL); 95 mHandleSubnormal = true; 96 mFloatSize = 64; 97 } 98 } 99 100 /** 101 * Sets whether we are testing a native_* function and how many ulp we allow 102 * for full and relaxed precision. 103 */ setPrecision(int fullUlpFactor, int relaxedUlpFactor)104 void setPrecision(int fullUlpFactor, int relaxedUlpFactor) { 105 mUlpFactor = mIsRelaxedPrecision ? relaxedUlpFactor : fullUlpFactor; 106 } 107 108 /** 109 * Helper functions to create a new Floaty with the current expected level of precision. 110 * We have variations that expect one to five arguments. Any of the passed arguments are 111 * considered valid values for that Floaty. 112 */ newFloaty(double a)113 Floaty newFloaty(double a) { 114 return new Floaty(mFloatSize, new double [] { a }); 115 } 116 newFloaty(double a, double b)117 Floaty newFloaty(double a, double b) { 118 return new Floaty(mFloatSize, new double [] { a, b }); 119 } 120 newFloaty(double a, double b, double c)121 Floaty newFloaty(double a, double b, double c) { 122 return new Floaty(mFloatSize, new double [] { a, b, c }); 123 } 124 newFloaty(double a, double b, double c, double d)125 Floaty newFloaty(double a, double b, double c, double d) { 126 return new Floaty(mFloatSize, new double [] { a, b, c, d }); 127 } 128 newFloaty(double a, double b, double c, double d, double e)129 Floaty newFloaty(double a, double b, double c, double d, double e) { 130 return new Floaty(mFloatSize, new double [] { a, b, c, d, e }); 131 } 132 133 /** 134 * Helper functions to create a new 32 bit Floaty with the current expected level of precision. 135 * We have variations that expect one to five arguments. Any of the passed arguments are considered 136 * valid values for that Floaty. 137 */ new32(float a)138 Floaty new32(float a) { 139 return new Floaty(32, new double [] { a }); 140 } 141 new32(float a, float b)142 Floaty new32(float a, float b) { 143 return new Floaty(32, new double [] { a, b }); 144 } 145 new32(float a, float b, float c)146 Floaty new32(float a, float b, float c) { 147 return new Floaty(32, new double [] { a, b, c }); 148 } 149 new32(float a, float b, float c, float d)150 Floaty new32(float a, float b, float c, float d) { 151 return new Floaty(32, new double [] { a, b, c, d }); 152 } 153 new32(float a, float b, float c, float d, float e)154 Floaty new32(float a, float b, float c, float d, float e) { 155 return new Floaty(32, new double [] { a, b, c, d, e }); 156 } 157 158 /** 159 * Helper functions to create a new 64 bit Floaty with the current expected level of precision. 160 * We have variations that expect one to five arguments. Any of the passed arguments are considered 161 * valid values for that Floaty. 162 */ new64(double a)163 Floaty new64(double a) { 164 return new Floaty(64, new double [] { a }); 165 } 166 new64(double a, double b)167 Floaty new64(double a, double b) { 168 return new Floaty(64, new double [] { a, b }); 169 } 170 new64(double a, double b, double c)171 Floaty new64(double a, double b, double c) { 172 return new Floaty(64, new double [] { a, b, c }); 173 } 174 new64(double a, double b, double c, double d)175 Floaty new64(double a, double b, double c, double d) { 176 return new Floaty(64, new double [] { a, b, c, d }); 177 } 178 new64(double a, double b, double c, double d, double e)179 Floaty new64(double a, double b, double c, double d, double e) { 180 return new Floaty(64, new double [] { a, b, c, d, e }); 181 } 182 183 /** 184 * Returns a Floaty that contain a NaN for the specified size. 185 */ newNan(int numberOfBits)186 Floaty newNan(int numberOfBits) { 187 return new Floaty(numberOfBits, new double [] { Double.NaN }); 188 } 189 add(Floaty a, Floaty b)190 Floaty add(Floaty a, Floaty b) { 191 //Log.w("Target.add", "a: " + a.toString()); 192 //Log.w("Target.add", "b: " + b.toString()); 193 assert(a.mNumberOfBits == b.mNumberOfBits); 194 if (!a.mHasRange || !b.mHasRange) { 195 return newNan(a.mNumberOfBits); 196 } 197 return new Floaty(a.mNumberOfBits, new double[] { a.mValue + b.mValue, 198 a.mMinValue + b.mMinValue, 199 a.mMaxValue + b.mMaxValue }); 200 } 201 subtract(Floaty a, Floaty b)202 Floaty subtract(Floaty a, Floaty b) { 203 //Log.w("Target.subtract", "a: " + a.toString()); 204 //Log.w("Target.subtract", "b: " + b.toString()); 205 assert(a.mNumberOfBits == b.mNumberOfBits); 206 if (!a.mHasRange || !b.mHasRange) { 207 return newNan(a.mNumberOfBits); 208 } 209 return new Floaty(a.mNumberOfBits, new double[] { a.mValue - b.mValue, 210 a.mMinValue - b.mMaxValue, 211 a.mMaxValue - b.mMinValue }); 212 } 213 multiply(Floaty a, Floaty b)214 Floaty multiply(Floaty a, Floaty b) { 215 //Log.w("Target.multiply", "a: " + a.toString()); 216 //Log.w("Target.multiply", "b: " + b.toString()); 217 assert(a.mNumberOfBits == b.mNumberOfBits); 218 if (!a.mHasRange || !b.mHasRange) { 219 return newNan(a.mNumberOfBits); 220 } 221 return new Floaty(a.mNumberOfBits, new double[] { a.mValue * b.mValue, 222 a.mMinValue * b.mMinValue, 223 a.mMinValue * b.mMaxValue, 224 a.mMaxValue * b.mMinValue, 225 a.mMaxValue * b.mMaxValue}); 226 } 227 divide(Floaty a, Floaty b)228 Floaty divide(Floaty a, Floaty b) { 229 //Log.w("Target.divide", "a: " + a.toString()); 230 //Log.w("Target.divide", "b: " + b.toString()); 231 assert(a.mNumberOfBits == b.mNumberOfBits); 232 if (!a.mHasRange || !b.mHasRange) { 233 return newNan(a.mNumberOfBits); 234 } 235 return new Floaty(a.mNumberOfBits, new double[] { a.mValue / b.mValue, 236 a.mMinValue / b.mMinValue, 237 a.mMinValue / b.mMaxValue, 238 a.mMaxValue / b.mMinValue, 239 a.mMaxValue / b.mMaxValue}); 240 } 241 242 /** Returns the absolute value of a Floaty. */ abs(Floaty a)243 Floaty abs(Floaty a) { 244 if (!a.mHasRange) { 245 return newNan(a.mNumberOfBits); 246 } 247 if (a.mMinValue >= 0 && a.mMaxValue >= 0) { 248 // Two non-negatives, no change 249 return a; 250 } 251 Floaty f = new Floaty(a); 252 f.mValue = Math.abs(a.mValue); 253 if (a.mMinValue < 0 && a.mMaxValue < 0) { 254 // Two negatives, we invert 255 f.mMinValue = -a.mMaxValue; 256 f.mMaxValue = -a.mMinValue; 257 } else { 258 // We have one negative, one positive. 259 f.mMinValue = 0.f; 260 f.mMaxValue = Math.max(-a.mMinValue, a.mMaxValue); 261 } 262 return f; 263 } 264 265 /** Returns the square root of a Floaty. */ sqrt(Floaty a)266 Floaty sqrt(Floaty a) { 267 //Log.w("Target.sqrt", "a: " + a.toString()); 268 if (!a.mHasRange) { 269 return newNan(a.mNumberOfBits); 270 } 271 double f = Math.sqrt(a.mValue); 272 double min = Math.sqrt(a.mMinValue); 273 double max = Math.sqrt(a.mMaxValue); 274 double[] values; 275 /* If the range of inputs covers 0, make sure we have it as one of 276 * the answers, to set the correct lowest bound, as the square root 277 * of the negative inputs will yield a NaN flag and won't affect the 278 * range. 279 */ 280 if (a.mMinValue < 0 && a.mMaxValue > 0) { 281 values = new double[]{f, 0., min, max}; 282 } else { 283 values = new double[]{f, min, max}; 284 } 285 Floaty answer = new Floaty(a.mNumberOfBits, values); 286 // Allow a little more imprecision for a square root operation. 287 answer.ExpandRangeByUlpFactor(); 288 return answer; 289 } 290 291 /** 292 * This class represents the range of floating point values we accept as the result of a 293 * computation performed by a runtime driver. 294 */ 295 class Floaty { 296 /** 297 * The number of bits the value should have, either 32 or 64. It would have been nice to 298 * use generics, e.g. Floaty<double> and Floaty<double> but Java does not support generics 299 * of float and double. Also, Java does not have an f16 type. This can simulate it, 300 * although more work will be needed. 301 */ 302 private int mNumberOfBits; 303 /** True if NaN is an acceptable value. */ 304 private boolean mCanBeNan; 305 /** 306 * True if mValue, mMinValue, mMaxValue have been set. This should be the case if mCanBeNan is false. 307 * It's possible for both mCanBeNan and mHasRange to be true at the same time. 308 */ 309 private boolean mHasRange; 310 /** 311 * The typical value we would expect. We don't just keep track of the min and max 312 * of the ranges of values allowed because some functions we are evaluating are 313 * discontinuous, e.g. sqrt around 0, lgamma around -1, -2, -3 and tan around pi/2. 314 * By keeping track of the middle value, we're more likely to handle this discontinuity 315 * correctly. 316 */ 317 private double mValue; 318 /** The minimum value we would expect. */ 319 private double mMinValue; 320 /** The maximum value we would expect. */ 321 private double mMaxValue; 322 Floaty(Floaty a)323 Floaty(Floaty a) { 324 mNumberOfBits = a.mNumberOfBits; 325 mCanBeNan = a.mCanBeNan; 326 mHasRange = a.mHasRange; 327 mValue = a.mValue; 328 mMinValue = a.mMinValue; 329 mMaxValue = a.mMaxValue; 330 } 331 332 /** 333 * Creates a Floaty and initializes it so that the values passed could be represented by it. 334 * We also expand what's allowed by +/- mUlpFactor to allow for the various rounding modes. 335 * values[0] is treated as the representative case, otherwise the order of values does not matter. 336 */ Floaty(int numberOfBits, double values[])337 Floaty(int numberOfBits, double values[]) { 338 //Log.w("Floaty(double[], ulp)", "input: " + Arrays.toString(values) + ", ulp " + Integer.toString(mUlpFactor)); 339 mNumberOfBits = numberOfBits; 340 mCanBeNan = false; 341 mHasRange = false; 342 mValue = values[0]; 343 for (double f: values) { 344 if (Double.isNaN(f)) { 345 mCanBeNan = true; 346 continue; 347 } 348 updateMinAndMax(f); 349 // For relaxed mode, we don't require support of subnormal values. 350 // If we have a subnormal value, we'll allow both the normalized value and zero, 351 // to cover the two ways this small value might be handled. 352 if (!mHandleSubnormal) { 353 if (IsSubnormal(f)) { 354 updateMinAndMax(0.f); 355 updateMinAndMax(smallestNormal(f)); 356 } 357 } 358 } 359 360 // Expand the range to the closest value representable in the desired floating-point 361 // format 362 ExpandRangeToTargetPrecision(); 363 364 // Expand the range by one ulp factor to cover for the different rounding modes. 365 ExpandRangeByUlpFactor(); 366 //Log.w("Floaty(double[], ulp)", "output: " + toString()); 367 } 368 369 /** Modify the mMinValue and mMaxValue so that f is contained within the range. */ updateMinAndMax(double f)370 private void updateMinAndMax(double f) { 371 if (mHasRange) { 372 if (f < mMinValue) { 373 mMinValue = f; 374 } 375 if (f > mMaxValue) { 376 mMaxValue = f; 377 } 378 } else { 379 mHasRange = true; 380 mMinValue = f; 381 mMaxValue = f; 382 } 383 } 384 385 /** Return (as double) the next highest value representable in Float16 precision */ roundFloat16Up(double value)386 private double roundFloat16Up(double value) { 387 return Float16Utils.roundToFloat16(value)[1]; 388 } 389 390 /** Return (as double) the next lowest value representable in Float16 precision */ roundFloat16Down(double value)391 private double roundFloat16Down(double value) { 392 return Float16Utils.roundToFloat16(value)[0]; 393 } 394 395 /** 396 * Modify mMinValue and mMaxValue to the closest value representable in mNumberOfBits. 397 * mMinValue is rounded down and mMaxValue is rounded u 398 */ ExpandRangeToTargetPrecision()399 void ExpandRangeToTargetPrecision() { 400 if (!mHasRange) return; 401 402 if (mNumberOfBits != 16) return; // For now, this function is only needed for Float16. 403 404 // Log.w("ExpandRangeToFloat16Precision", "Before: " + Double.toString(mMinValue) + " " + Double.toString(mValue) + " " + Double.toString(mMaxValue)); 405 406 // TODO Should we adjust mValue to Float16-representable value? 407 mMaxValue = roundFloat16Up(mMaxValue); 408 mMinValue = roundFloat16Down(mMinValue); 409 410 //Log.w("ExpandRangeToFloat16Precision", "After: " + Double.toString(mMinValue) + " " + Double.toString(mValue) + " " + Double.toString(mMaxValue)); 411 } 412 413 /** Modify mMinValue and mMaxValue to allow one extra ulp factor of error on each side. */ ExpandRangeByUlpFactor()414 void ExpandRangeByUlpFactor() { 415 if (mHasRange && mUlpFactor > 0) { 416 // Expand the edges by the specified factor. 417 ExpandMin(mUlpFactor); 418 ExpandMax(mUlpFactor); 419 } 420 } 421 422 /** Expand the mMinValue by the number of ulp specified. */ ExpandMin(int ulpFactor)423 private void ExpandMin(int ulpFactor) { 424 //Log.w("ExpandMin", java.lang.Double.toString(mMinValue) + " by " + Integer.toString(ulpFactor)); 425 if (!mHasRange) { 426 return; 427 } 428 if (mMinValue == Double.NEGATIVE_INFINITY || 429 mMinValue == Double.POSITIVE_INFINITY) { 430 // Can't get any larger 431 //Log.w("ExpandMin", "infinity"); 432 return; 433 } 434 double ulp = NegativeUlp(); 435 double delta = ulp * ulpFactor; 436 double newValue = mMinValue + delta; 437 /* 438 * Reduce mMinValue but don't go negative if it's positive because the rounding error 439 * we're simulating won't change the sign. 440 */ 441 if (newValue < 0 && mMinValue > 0.f) { 442 mMinValue = 0.f; 443 } else { 444 mMinValue = newValue; 445 } 446 // If subnormal, also allow the normalized value if it's smaller. 447 if (!mHandleSubnormal && IsSubnormal(mMinValue)) { 448 if (mMinValue < 0) { 449 mMinValue = smallestNormal(-1.0f); 450 } else { 451 mMinValue = 0.f; 452 } 453 } 454 455 // If Float16, round minValue down to maintain invariant that the range is always 456 // representable in Float16. 457 if (mNumberOfBits == 16) { 458 mMinValue = roundFloat16Down(mMinValue); 459 } 460 //Log.w("ExpandMin", "ulp " + java.lang.Double.toString(ulp) + ", delta " + java.lang.Double.toString(delta) + " for " + java.lang.Double.toString(mMinValue)); 461 } 462 463 /** Expand the mMaxValue by the number of ulp specified. */ ExpandMax(int ulpFactor)464 private void ExpandMax(int ulpFactor) { 465 //Log.w("ExpandMax", java.lang.Double.toString(mMaxValue) + " by " + Integer.toString(ulpFactor)); 466 if (!mHasRange) { 467 return; 468 } 469 if (mMaxValue == Double.NEGATIVE_INFINITY || 470 mMaxValue == Double.POSITIVE_INFINITY) { 471 // Can't get any larger 472 //Log.w("ExpandMax", "infinity"); 473 return; 474 } 475 double ulp = Ulp(); 476 double delta = ulp * ulpFactor; 477 double newValue = mMaxValue + delta; 478 /* 479 * Increase mMaxValue but don't go positive if it's negative because the rounding error 480 * we're simulating won't change the sign. 481 */ 482 if (newValue > 0 && mMaxValue < 0.f) { 483 mMaxValue = 0.f; 484 } else { 485 mMaxValue = newValue; 486 } 487 // If subnormal, also allow the normalized value if it's smaller. 488 if (!mHandleSubnormal && IsSubnormal(mMaxValue)) { 489 if (mMaxValue > 0) { 490 mMaxValue = smallestNormal(1.0f); 491 } else { 492 mMaxValue = 0.f; 493 } 494 } 495 496 // If Float16, round mMaxValue up to maintain invariant that the range is always 497 // representable in Float16. 498 if (mNumberOfBits == 16 && mMaxValue != 0) { 499 mMaxValue = roundFloat16Up(mMaxValue); 500 } 501 //Log.w("ExpandMax", "ulp " + java.lang.Double.toString(ulp) + ", delta " + java.lang.Double.toString(delta) + " for " + java.lang.Double.toString(mMaxValue)); 502 } 503 504 /** 505 * Returns true if f is smaller than the smallest normalized number that can be represented 506 * by the number of bits we have. 507 */ IsSubnormal(double f)508 private boolean IsSubnormal(double f) { 509 double af = Math.abs(f); 510 return 0 < af && af < smallestNormal(1.0f); 511 } 512 513 /** 514 * Returns the smallest normal representable by the number of bits we have, of the same 515 * sign as f. 516 */ smallestNormal(double f)517 private double smallestNormal(double f) { 518 double answer; 519 if (mNumberOfBits == 16) { 520 answer = Float16Utils.MIN_NORMAL; 521 } else if (mNumberOfBits == 32) { 522 answer = Float.MIN_NORMAL; 523 } else { 524 answer = Double.MIN_NORMAL; 525 } 526 if (f < 0) { 527 answer = -answer; 528 } 529 return answer; 530 } 531 532 /** Returns the unit of least precision for the maximum value allowed. */ Ulp()533 private double Ulp() { 534 double u; 535 if (mNumberOfBits == 16) { 536 u = Float16Utils.float16Ulp(mMaxValue); 537 } else if (mNumberOfBits == 32) { 538 u = Math.ulp((float)mMaxValue); 539 } else { 540 u = Math.ulp(mMaxValue); 541 } 542 if (!mHandleSubnormal) { 543 u = Math.max(u, smallestNormal(1.f)); 544 } 545 return u; 546 } 547 548 /** Returns the negative of the unit of least precision for the minimum value allowed. */ NegativeUlp()549 private double NegativeUlp() { 550 double u; 551 if (mNumberOfBits == 16) { 552 u = -Float16Utils.float16Ulp(mMinValue); 553 } else if (mNumberOfBits == 32) { 554 u = -Math.ulp((float)mMinValue); 555 } else { 556 u = -Math.ulp(mMinValue); 557 } 558 if (!mHandleSubnormal) { 559 u = Math.min(u, smallestNormal(-1.f)); 560 } 561 return u; 562 } 563 564 /** Returns true if the number passed is among the values that are represented by this Floaty. */ couldBe(double a)565 public boolean couldBe(double a) { 566 return couldBe(a, 0.0); 567 } 568 569 /** 570 * Returns true if the number passed is among the values that are represented by this Floaty. 571 * An extra amount of allowed error can be specified. 572 */ couldBe(double a, double extraAllowedError)573 public boolean couldBe(double a, double extraAllowedError) { 574 //Log.w("Floaty.couldBe", "Can " + Double.toString(a) + " be " + toString() + "? "); 575 // Handle the input being a NaN. 576 if (Double.isNaN(a)) { 577 //Log.w("couldBe", "true because is Naan"); 578 return mCanBeNan; 579 } 580 // If the input is not a NaN, we better have a range. 581 if (!mHasRange) { 582 return false; 583 } 584 // Handle the simple case. 585 if ((mMinValue - extraAllowedError) <= a && a <= (mMaxValue + extraAllowedError)) { 586 return true; 587 } 588 // For native, we don't require returning +/- infinity. If that's what we expect, 589 // allow all answers. 590 if (mFunctionType == FunctionType.NATIVE) { 591 if (mMinValue == Double.NEGATIVE_INFINITY && 592 mMaxValue == Double.NEGATIVE_INFINITY) { 593 return true; 594 } 595 if (mMinValue == Double.POSITIVE_INFINITY && 596 mMaxValue == Double.POSITIVE_INFINITY) { 597 return true; 598 } 599 } 600 return false; 601 } 602 603 604 // TODO simplify: remove 32() and 64() get()605 public double get() { return mValue; } mid()606 public double mid() { return mid64(); } min()607 public double min() { return mMinValue; } max()608 public double max() { return mMaxValue; } 609 get64()610 public double get64() { return mValue; } min64()611 public double min64() { return mMinValue; } max64()612 public double max64() { return mMaxValue; } 613 /** 614 * Returns mValue unless zero could be a legal value. In that case, return it. 615 * This is useful for testing functions where the behavior at zero is unusual, 616 * e.g. reciprocals. If mValue is already +0.0 or -0.0, don't change it, to 617 * preserve the sign. 618 */ mid64()619 public double mid64() { 620 if (mMinValue < 0.0 && mMaxValue > 0.0 && mValue != 0.0) { 621 return 0.0; 622 } 623 return mValue; 624 } 625 get32()626 public float get32() { return (float) mValue; } min32()627 public float min32() { return (float) mMinValue; } max32()628 public float max32() { return (float) mMaxValue; } mid32()629 public float mid32() { return (float) mid64(); } 630 toString()631 public String toString() { 632 String s = String.format("[f%d: ", mNumberOfBits); 633 if (mCanBeNan) { 634 s += "NaN, "; 635 } 636 if (mHasRange) { 637 if (mNumberOfBits == 32) { 638 float min = (float)mMinValue; 639 float mid = (float)mValue; 640 float max = (float)mMaxValue; 641 s += String.format("%11.9g {%08x} to %11.9g {%08x} to %11.9g {%08x}", min, 642 Float.floatToRawIntBits(min), mid, 643 Float.floatToRawIntBits(mid), max, 644 Float.floatToRawIntBits(max)); 645 } else { 646 s += String.format("%24.9g {%16x} to %24.9g {%16x} to %24.9g {%16x}", mMinValue, 647 Double.doubleToRawLongBits(mMinValue), mValue, 648 Double.doubleToRawLongBits(mValue), mMaxValue, 649 Double.doubleToRawLongBits(mMaxValue)); 650 } 651 } 652 s += "]"; 653 return s; 654 } 655 } 656 } 657