tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

tcuFloatFormat.js (13418B)


      1 /*-------------------------------------------------------------------------
      2 * drawElements Quality Program Tester Core
      3 * ----------------------------------------
      4 *
      5 * Copyright 2014 The Android Open Source Project
      6 *
      7 * Licensed under the Apache License, Version 2.0 (the "License");
      8 * you may not use this file except in compliance with the License.
      9 * You may obtain a copy of the License at
     10 *
     11 *      http://www.apache.org/licenses/LICENSE-2.0
     12 *
     13 * Unless required by applicable law or agreed to in writing, software
     14 * distributed under the License is distributed on an "AS IS" BASIS,
     15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     16 * See the License for the specific language governing permissions and
     17 * limitations under the License.
     18 *
     19 *//*!
     20 * \file
     21 * \brief Adjustable-precision floating point operations.
     22 *//*--------------------------------------------------------------------*/
     23 'use strict';
     24 goog.provide('framework.common.tcuFloatFormat');
     25 
     26 goog.require('framework.common.tcuInterval');
     27 goog.require('framework.delibs.debase.deMath');
     28 
     29 goog.scope(function() {
     30 
     31     var tcuFloatFormat = framework.common.tcuFloatFormat;
     32     var deMath = framework.delibs.debase.deMath;
     33     var tcuInterval = framework.common.tcuInterval;
     34 
     35     /**
     36      * @param {tcuFloatFormat.YesNoMaybe} choice
     37      * @param {tcuInterval.Interval} no
     38      * @param {tcuInterval.Interval} yes
     39      * @return {tcuInterval.Interval}
     40      */
     41    tcuFloatFormat.chooseInterval = function(choice, no, yes) {
     42        switch (choice) {
     43            case tcuFloatFormat.YesNoMaybe.NO: return no;
     44            case tcuFloatFormat.YesNoMaybe.YES: return yes;
     45            case tcuFloatFormat.YesNoMaybe.MAYBE: return no.operatorOrBinary(yes);
     46            default: throw new Error('Impossible case');
     47        }
     48    };
     49 
     50    /**
     51     * @param {number} maxExp
     52     * @param {number} fractionBits
     53     * @return {number}
     54     */
     55    tcuFloatFormat.computeMaxValue = function(maxExp, fractionBits) {
     56        return deMath.deLdExp(1, maxExp) + deMath.deLdExp(Math.pow(2, fractionBits) - 1, maxExp - fractionBits);
     57    };
     58 
     59     /**
     60      * @enum {number}
     61      */
     62     tcuFloatFormat.YesNoMaybe = {
     63        NO: 0,
     64         MAYBE: 1,
     65         YES: 2
     66     };
     67 
     68     /**
     69      * @constructor
     70      * @param {number} minExp
     71      * @param {number} maxExp
     72      * @param {number} fractionBits
     73      * @param {boolean} exactPrecision
     74      * @param {tcuFloatFormat.YesNoMaybe=} hasSubnormal
     75      * @param {tcuFloatFormat.YesNoMaybe=} hasInf
     76      * @param {tcuFloatFormat.YesNoMaybe=} hasNaN
     77      */
     78     tcuFloatFormat.FloatFormat = function(minExp, maxExp, fractionBits, exactPrecision, hasSubnormal, hasInf, hasNaN) {
     79         // /** @type{number} */ var exponentShift (int exp) const;
     80         // Interval clampValue (double d) const;
     81 
     82         /** @type {number} */ this.m_minExp = minExp; // Minimum exponent, inclusive
     83         /** @type {number} */ this.m_maxExp = maxExp; // Maximum exponent, inclusive
     84         /** @type {number} */ this.m_fractionBits = fractionBits; // Number of fractional bits in significand
     85         /** @type {tcuFloatFormat.YesNoMaybe} */ this.m_hasSubnormal = hasSubnormal === undefined ? tcuFloatFormat.YesNoMaybe.MAYBE : hasSubnormal; // Does the format support denormalized numbers?
     86         /** @type {tcuFloatFormat.YesNoMaybe} */ this.m_hasInf = hasInf === undefined ? tcuFloatFormat.YesNoMaybe.MAYBE : hasInf; // Does the format support infinities?
     87         /** @type {tcuFloatFormat.YesNoMaybe} */ this.m_hasNaN = hasNaN === undefined ? tcuFloatFormat.YesNoMaybe.MAYBE : hasNaN; // Does the format support NaNs?
     88         /** @type {boolean} */ this.m_exactPrecision = exactPrecision; // Are larger precisions disallowed?
     89         /** @type {number} */ this.m_maxValue = tcuFloatFormat.computeMaxValue(maxExp, fractionBits);
     90     };
     91 
     92     /**
     93      * @return {number}
     94      */
     95     tcuFloatFormat.FloatFormat.prototype.getMinExp = function() {
     96         return this.m_minExp;
     97     };
     98 
     99     /**
    100      * @return {number}
    101      */
    102     tcuFloatFormat.FloatFormat.prototype.getMaxExp = function() {
    103         return this.m_maxExp;
    104     };
    105 
    106     /**
    107      * @return {number}
    108      */
    109     tcuFloatFormat.FloatFormat.prototype.getMaxValue = function() {
    110         return this.m_maxValue;
    111     };
    112 
    113     /**
    114      * @return {number}
    115      */
    116     tcuFloatFormat.FloatFormat.prototype.getFractionBits = function() {
    117         return this.m_fractionBits;
    118     };
    119 
    120     /**
    121      * @return {tcuFloatFormat.YesNoMaybe}
    122      */
    123     tcuFloatFormat.FloatFormat.prototype.hasSubnormal = function() {
    124         return this.m_hasSubnormal;
    125     };
    126 
    127     /**
    128      * @return {tcuFloatFormat.YesNoMaybe}
    129      */
    130     tcuFloatFormat.FloatFormat.prototype.hasInf = function() {
    131         return this.m_hasInf;
    132     };
    133 
    134     /**
    135      * @param {number} x
    136      * @param {number} count
    137      * @return {number}
    138      */
    139     tcuFloatFormat.FloatFormat.prototype.ulp = function(x, count) {
    140        var breakdown = deMath.deFractExp(Math.abs(x));
    141         /** @type {number} */ var exp = breakdown.exponent;
    142         /** @type {number} */ var frac = breakdown.significand;
    143 
    144         if (isNaN(frac))
    145             return NaN;
    146         else if (!isFinite(frac))
    147             return deMath.deLdExp(1.0, this.m_maxExp - this.m_fractionBits);
    148         else if (frac == 1.0) {
    149             // Harrison's ULP: choose distance to closest (i.e. next lower) at binade
    150             // boundary.
    151             --exp;
    152         } else if (frac == 0.0)
    153             exp = this.m_minExp;
    154 
    155         // ULP cannot be lower than the smallest quantum.
    156         exp = Math.max(exp, this.m_minExp);
    157 
    158         /** @type {number} */ var oneULP = deMath.deLdExp(1.0, exp - this.m_fractionBits);
    159         //     ScopedRoundingMode ctx (DE_ROUNDINGMODE_TO_POSITIVE_INF);
    160 
    161         return oneULP * count;
    162    };
    163 
    164    /**
    165     * Return the difference between the given nominal exponent and
    166     * the exponent of the lowest significand bit of the
    167     * representation of a number with this format.
    168     * For normal numbers this is the number of significand bits, but
    169     * for subnormals it is less and for values of exp where 2^exp is too
    170     * small to represent it is <0
    171     * @param {number} exp
    172     * @return {number}
    173     */
    174    tcuFloatFormat.FloatFormat.prototype.exponentShift = function(exp) {
    175        return this.m_fractionBits - Math.max(this.m_minExp - exp, 0);
    176    };
    177 
    178    /**
    179     * @param {number} d
    180     * @param {boolean} upward
    181     * @return {number}
    182     */
    183    tcuFloatFormat.FloatFormat.prototype.round = function(d, upward) {
    184        var breakdown = deMath.deFractExp(d);
    185        /** @type {number} */ var exp = breakdown.exponent;
    186        /** @type {number} */ var frac = breakdown.significand;
    187 
    188        var shift = this.exponentShift(exp);
    189        var shiftFrac = deMath.deLdExp(frac, shift);
    190        var roundFrac = upward ? Math.ceil(shiftFrac) : Math.floor(shiftFrac);
    191 
    192        return deMath.deLdExp(roundFrac, exp - shift);
    193    };
    194 
    195    /**
    196     * Return the range of numbers that `d` might be converted to in the
    197     * floatformat, given its limitations with infinities, subnormals and maximum
    198     * exponent.
    199     * @param {number} d
    200     * @return {tcuInterval.Interval}
    201     */
    202     tcuFloatFormat.FloatFormat.prototype.clampValue = function(d) {
    203        /** @type {number} */ var rSign = deMath.deSign(d);
    204        /** @type {number} */ var rExp = 0;
    205 
    206        // DE_ASSERT(!isNaN(d));
    207 
    208        var breakdown = deMath.deFractExp(d);
    209        rExp = breakdown.exponent;
    210        if (rExp < this.m_minExp)
    211            return tcuFloatFormat.chooseInterval(this.m_hasSubnormal, new tcuInterval.Interval(rSign * 0.0), new tcuInterval.Interval(d));
    212        else if (!isFinite(d) || rExp > this.m_maxExp)
    213            return tcuFloatFormat.chooseInterval(this.m_hasInf, new tcuInterval.Interval(rSign * this.getMaxValue()), new tcuInterval.Interval(rSign * Number.POSITIVE_INFINITY));
    214 
    215        return new tcuInterval.Interval(d);
    216    };
    217 
    218    /**
    219     * @param {number} d
    220     * @param {boolean} upward
    221     * @param {boolean} roundUnderOverflow
    222     * @return {number}
    223     */
    224    tcuFloatFormat.FloatFormat.prototype.roundOutDir = function(d, upward, roundUnderOverflow) {
    225        var breakdown = deMath.deFractExp(d);
    226        var exp = breakdown.exponent;
    227 
    228        if (roundUnderOverflow && exp > this.m_maxExp && (upward == (d < 0.0)))
    229            return deMath.deSign(d) * this.getMaxValue();
    230        else
    231            return this.round(d, upward);
    232    };
    233 
    234    /**
    235     * @param {tcuInterval.Interval} x
    236     * @param {boolean} roundUnderOverflow
    237     * @return {tcuInterval.Interval}
    238     */
    239    tcuFloatFormat.FloatFormat.prototype.roundOut = function(x, roundUnderOverflow) {
    240        /** @type {tcuInterval.Interval} */ var ret = x.nan();
    241 
    242        if (!x.empty()) {
    243            var a = new tcuInterval.Interval(this.roundOutDir(x.lo(), false, roundUnderOverflow));
    244            var b = new tcuInterval.Interval(this.roundOutDir(x.hi(), true, roundUnderOverflow));
    245            ret.operatorOrAssignBinary(tcuInterval.withIntervals(a, b));
    246        }
    247        return ret;
    248    };
    249 
    250    //! Return the range of numbers that might be used with this format to
    251    //! represent a number within `x`.
    252    /**
    253     * @param {tcuInterval.Interval} x
    254     * @return {tcuInterval.Interval}
    255     */
    256    tcuFloatFormat.FloatFormat.prototype.convert = function(x) {
    257        /** @type {tcuInterval.Interval} */ var ret = new tcuInterval.Interval();
    258        /** @type {tcuInterval.Interval} */ var tmp = x;
    259 
    260        if (x.hasNaN()) {
    261            // If NaN might be supported, NaN is a legal return value
    262            if (this.m_hasNaN != tcuFloatFormat.YesNoMaybe.NO)
    263                ret.operatorOrAssignBinary(new tcuInterval.Interval(NaN));
    264 
    265            // If NaN might not be supported, any (non-NaN) value is legal,
    266            // _subject_ to clamping. Hence we modify tmp, not ret.
    267            if (this.m_hasNaN != tcuFloatFormat.YesNoMaybe.YES)
    268                tmp = tcuInterval.unbounded();
    269        }
    270 
    271        // Round both bounds _inwards_ to closest representable values.
    272        if (!tmp.empty())
    273            ret.operatorOrAssignBinary(
    274                this.clampValue(this.round(tmp.lo(), true)).operatorOrBinary(
    275                    this.clampValue(this.round(tmp.hi(), false))));
    276 
    277        // If this format's precision is not exact, the (possibly out-of-bounds)
    278        // original value is also a possible result.
    279        if (!this.m_exactPrecision)
    280            ret.operatorOrAssignBinary(x);
    281 
    282        return ret;
    283    };
    284 
    285    /**
    286     * @param {number} x
    287     * @return {string}
    288     */
    289    tcuFloatFormat.FloatFormat.prototype.floatToHex = function(x) {
    290        if (isNaN(x))
    291            return 'NaN';
    292        else if (!isFinite(x))
    293            return (x < 0.0 ? '-' : '+') + ('inf');
    294        else if (x == 0.0) // \todo [2014-03-27 lauri] Negative zero
    295            return '0.0';
    296 
    297        return x.toString(10);
    298        // TODO
    299        // var breakdown = deMath.deFractExp(deAbs(x));
    300        // /** @type{number} */ var exp = breakdown.exponent;
    301        // /** @type{number} */ var frac = breakdown.significand;
    302        // /** @type{number} */ var shift = this.exponentShift(exp);
    303        // /** @type{number} */ var bits = deUint64(deLdExp(frac, shift));
    304        // /** @type{number} */ var whole = bits >> m_fractionBits;
    305        // /** @type{number} */ var fraction = bits & ((deUint64(1) << m_fractionBits) - 1);
    306        // /** @type{number} */ var exponent = exp + m_fractionBits - shift;
    307        // /** @type{number} */ var numDigits = (this.m_fractionBits + 3) / 4;
    308        // /** @type{number} */ var aligned = fraction << (numDigits * 4 - m_fractionBits);
    309        // /** @type{string} */ var oss = '';
    310 
    311        // oss + (x < 0 ? '-' : '')
    312        //     + '0x' + whole + '.'
    313        //     + std::hex + std::setw(numDigits) + std::setfill('0') + aligned
    314        //     + 'p' + std::dec + std::setw(0) + exponent;
    315        //return oss;
    316    };
    317 
    318    /**
    319     * @param {tcuInterval.Interval} interval
    320     * @return {string}
    321     */
    322    tcuFloatFormat.FloatFormat.prototype.intervalToHex = function(interval) {
    323        if (interval.empty())
    324            return interval.hasNaN() ? '{ NaN }' : '{}';
    325 
    326        else if (interval.lo() == interval.hi())
    327            return ((interval.hasNaN() ? '{ NaN, ' : '{ ') +
    328                    this.floatToHex(interval.lo()) + ' }');
    329        else if (interval == tcuInterval.unbounded(true))
    330            return '<any>';
    331 
    332        return ((interval.hasNaN() ? '{ NaN } | ' : '') +
    333                '[' + this.floatToHex(interval.lo()) + ', ' + this.floatToHex(interval.hi()) + ']');
    334    };
    335 
    336    /**
    337     * @return {tcuFloatFormat.FloatFormat}
    338     */
    339    tcuFloatFormat.nativeDouble = function() {
    340        return new tcuFloatFormat.FloatFormat(-1021 - 1, // min_exponent
    341                                              1024 - 1, // max_exponent
    342                                              53 - 1, // digits
    343                                              true, // has_denorm
    344                                              tcuFloatFormat.YesNoMaybe.YES, // has_infinity
    345                                              tcuFloatFormat.YesNoMaybe.YES, // has_quiet_nan
    346                                              tcuFloatFormat.YesNoMaybe.YES); // has_denorm
    347    };
    348 
    349 });