Home | History | Annotate | Download | only in cts
      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 
     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      */
    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      */
    113     Floaty newFloaty(double a) {
    114         return new Floaty(mFloatSize, new double [] { a });
    115     }
    116 
    117     Floaty newFloaty(double a, double b) {
    118         return new Floaty(mFloatSize, new double [] { a, b });
    119     }
    120 
    121     Floaty newFloaty(double a, double b, double c) {
    122         return new Floaty(mFloatSize, new double [] { a, b, c });
    123     }
    124 
    125     Floaty newFloaty(double a, double b, double c, double d) {
    126         return new Floaty(mFloatSize, new double [] { a, b, c, d });
    127     }
    128 
    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      */
    138     Floaty new32(float a) {
    139         return new Floaty(32, new double [] { a });
    140     }
    141 
    142     Floaty new32(float a, float b) {
    143         return new Floaty(32, new double [] { a, b });
    144     }
    145 
    146     Floaty new32(float a, float b, float c) {
    147         return new Floaty(32, new double [] { a, b, c });
    148     }
    149 
    150     Floaty new32(float a, float b, float c, float d) {
    151         return new Floaty(32, new double [] { a, b, c, d });
    152     }
    153 
    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      */
    163     Floaty new64(double a) {
    164         return new Floaty(64, new double [] { a });
    165     }
    166 
    167     Floaty new64(double a, double b) {
    168         return new Floaty(64, new double [] { a, b });
    169     }
    170 
    171     Floaty new64(double a, double b, double c) {
    172         return new Floaty(64, new double [] { a, b, c });
    173     }
    174 
    175     Floaty new64(double a, double b, double c, double d) {
    176         return new Floaty(64, new double [] { a, b, c, d });
    177     }
    178 
    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      */
    186     Floaty newNan(int numberOfBits) {
    187         return new Floaty(numberOfBits, new double [] { Double.NaN });
    188     }
    189 
    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 
    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 
    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 
    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. */
    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. */
    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 
    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          */
    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 (f != 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. */
    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 */
    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 */
    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          */
    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. */
    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. */
    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. */
    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          */
    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          */
    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. */
    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. */
    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. */
    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          */
    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 (a != 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()
    605         public double get() { return mValue; }
    606         public double mid() { return mid64(); }
    607         public double min() { return mMinValue; }
    608         public double max() { return mMaxValue; }
    609 
    610         public double get64() { return mValue; }
    611         public double min64() { return mMinValue; }
    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          */
    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 
    626         public float get32() { return (float) mValue; }
    627         public float min32() { return (float) mMinValue; }
    628         public float max32() { return (float) mMaxValue; }
    629         public float mid32() { return (float) mid64(); }
    630 
    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