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