/*-------------------------------------------------------------------------
 * drawElements Quality Program OpenGL ES Utilities
 * ------------------------------------------------
 *
 * Copyright 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

'use strict';
goog.provide('framework.common.tcuFloat');
goog.require('framework.delibs.debase.deMath');

goog.scope(function() {

var tcuFloat = framework.common.tcuFloat;
var deMath = framework.delibs.debase.deMath;

var DE_ASSERT = function(x) {
    if (!x)
        throw new Error('Assert failed');
};

tcuFloat.FloatFlags = {
    FLOAT_HAS_SIGN: (1 << 0),
    FLOAT_SUPPORT_DENORM: (1 << 1)
};

/**
 * Defines a tcuFloat.FloatDescription object, which is an essential part of the tcuFloat.deFloat type.
 * Holds the information that shapes the tcuFloat.deFloat.
 * @constructor
 */
tcuFloat.FloatDescription = function(exponentBits, mantissaBits, exponentBias, flags) {
    this.ExponentBits = exponentBits;
    this.MantissaBits = mantissaBits;
    this.ExponentBias = exponentBias;
    this.Flags = flags;

    this.totalBitSize = 1 + this.ExponentBits + this.MantissaBits;
    this.totalByteSize = Math.floor(this.totalBitSize / 8) + ((this.totalBitSize % 8) > 0 ? 1 : 0);
};

/**
 * Builds a zero float of the current binary description.
 * @param {number} sign
 * @return {tcuFloat.deFloat}
 */
tcuFloat.FloatDescription.prototype.zero = function(sign) {
    return tcuFloat.newDeFloatFromParameters(this.zeroNumber(sign), this);
};

tcuFloat.FloatDescription.prototype.zeroNumber = function(sign) {
    return deMath.shiftLeft((sign > 0 ? 0 : 1), (this.ExponentBits + this.MantissaBits));
};

/**
 * Builds an infinity float representation of the current binary description.
 * @param {number} sign
 * @return {tcuFloat.deFloat}
 */
tcuFloat.FloatDescription.prototype.inf = function(sign) {
    return tcuFloat.newDeFloatFromParameters(this.infNumber(sign), this);
};

tcuFloat.FloatDescription.prototype.infNumber = function(sign) {
    return ((sign > 0 ? 0 : 1) << (this.ExponentBits + this.MantissaBits)) |
        deMath.shiftLeft(((1 << this.ExponentBits) - 1), this.MantissaBits); //Unless using very large exponent types, native shift is safe here, i guess.
};

/**
 * Builds a NaN float representation of the current binary description.
 * @return {tcuFloat.deFloat}
 */
tcuFloat.FloatDescription.prototype.nan = function() {
    return tcuFloat.newDeFloatFromParameters(this.nanNumber(), this);
};

tcuFloat.FloatDescription.prototype.nanNumber = function() {
    return deMath.shiftLeft(1, (this.ExponentBits + this.MantissaBits)) - 1;
};

/**
 * Builds a tcuFloat.deFloat number based on the description and the given
 * sign, exponent and mantissa values.
 * @param {number} sign
 * @param {number} exponent
 * @param {number} mantissa
 * @return {tcuFloat.deFloat}
 */
tcuFloat.FloatDescription.prototype.construct = function(sign, exponent, mantissa) {
    // Repurpose this otherwise invalid input as a shorthand notation for zero (no need for caller to care about internal representation)
    /** @type {boolean} */ var isShorthandZero = exponent == 0 && mantissa == 0;

    // Handles the typical notation for zero (min exponent, mantissa 0). Note that the exponent usually used exponent (-ExponentBias) for zero/subnormals is not used.
    // Instead zero/subnormals have the (normally implicit) leading mantissa bit set to zero.

    /** @type {boolean} */ var isDenormOrZero = (exponent == 1 - this.ExponentBias) && (deMath.shiftRight(mantissa, this.MantissaBits) == 0);
    /** @type {number} */ var s = deMath.shiftLeft((sign < 0 ? 1 : 0), (this.ExponentBits + this.MantissaBits));
    /** @type {number} */ var exp = (isShorthandZero || isDenormOrZero) ? 0 : exponent + this.ExponentBias;

    DE_ASSERT(sign == +1 || sign == -1);
    DE_ASSERT(isShorthandZero || isDenormOrZero || deMath.shiftRight(mantissa, this.MantissaBits) == 1);
    DE_ASSERT((exp >> this.ExponentBits) == 0); //Native shift is safe

    return tcuFloat.newDeFloatFromParameters(
        deMath.binaryOp(
            deMath.binaryOp(
                s,
                deMath.shiftLeft(exp, this.MantissaBits),
                deMath.BinaryOp.OR
            ),
            deMath.binaryOp(
                mantissa,
                deMath.shiftLeft(1, this.MantissaBits) - 1,
                deMath.BinaryOp.AND
            ),
            deMath.BinaryOp.OR
        ),
        this
    );
};

/**
 * Builds a tcuFloat.deFloat number based on the description and the given
 * sign, exponent and binary mantissa values.
 * @param {number} sign
 * @param {number} exponent
 * @param {number} mantissaBits The raw binary representation.
 * @return {tcuFloat.deFloat}
 */
tcuFloat.FloatDescription.prototype.constructBits = function(sign, exponent, mantissaBits) {
    /** @type {number} */ var signBit = sign < 0 ? 1 : 0;
    /** @type {number} */ var exponentBits = exponent + this.ExponentBias;

    DE_ASSERT(sign == +1 || sign == -1);
    DE_ASSERT((exponentBits >> this.ExponentBits) == 0);
    DE_ASSERT(deMath.shiftRight(mantissaBits, this.MantissaBits) == 0);

    return tcuFloat.newDeFloatFromParameters(
        deMath.binaryOp(
            deMath.binaryOp(
                deMath.shiftLeft(
                    signBit,
                    this.ExponentBits + this.MantissaBits
                ),
                deMath.shiftLeft(exponentBits, this.MantissaBits),
                deMath.BinaryOp.OR
            ),
            mantissaBits,
            deMath.BinaryOp.OR
        ),
        this
    );
};

/**
 * Converts a tcuFloat.deFloat from it's own format description into the format described
 * by this description.
 * @param {tcuFloat.deFloat} other Other float to convert to this format description.
 * @return {tcuFloat.deFloat} converted tcuFloat.deFloat
 */
tcuFloat.FloatDescription.prototype.convert = function(other) {
    /** @type {number} */ var otherExponentBits = other.description.ExponentBits;
    /** @type {number} */ var otherMantissaBits = other.description.MantissaBits;
    /** @type {number} */ var otherExponentBias = other.description.ExponentBias;
    /** @type {number} */ var otherFlags = other.description.Flags;

    /** @type {number} */ var bitDiff;
    /** @type {number} */ var half;
    /** @type {number} */ var bias;

    if (!(this.Flags & tcuFloat.FloatFlags.FLOAT_HAS_SIGN) && other.sign() < 0) {
        // Negative number, truncate to zero.
        return this.zero(+1);
    } else if (other.isInf()) {
        return this.inf(other.sign());
    } else if (other.isNaN()) {
        return this.nan();
    } else if (other.isZero()) {
        return this.zero(other.sign());
    } else {
        /** @type {number} */ var eMin = 1 - this.ExponentBias;
        /** @type {number} */ var eMax = ((1 << this.ExponentBits) - 2) - this.ExponentBias;

        /** @type {number} */ var s = deMath.shiftLeft(other.signBit(), (this.ExponentBits + this.MantissaBits)); // \note Not sign, but sign bit.
        /** @type {number} */ var e = other.exponent();
        /** @type {number} */ var m = other.mantissa();

        // Normalize denormalized values prior to conversion.
        while (!deMath.binaryOp(m, deMath.shiftLeft(1, otherMantissaBits), deMath.BinaryOp.AND)) {
            m = deMath.shiftLeft(m, 1);
            e -= 1;
        }

        if (e < eMin) {
            // Underflow.
            if ((this.Flags & tcuFloat.FloatFlags.FLOAT_SUPPORT_DENORM) && (eMin - e - 1 <= this.MantissaBits)) {
                // Shift and round (RTE).
                bitDiff = (otherMantissaBits - this.MantissaBits) + (eMin - e);
                half = deMath.shiftLeft(1, (bitDiff - 1)) - 1;
                bias = deMath.binaryOp(deMath.shiftRight(m, bitDiff), 1, deMath.BinaryOp.AND);

                return tcuFloat.newDeFloatFromParameters(
                    deMath.binaryOp(
                        s,
                        deMath.shiftRight(
                            m + half + bias,
                            bitDiff
                        ),
                        deMath.BinaryOp.OR
                    ),
                    this
                );
            } else
                return this.zero(other.sign());
        } else {
            // Remove leading 1.
            m = deMath.binaryOp(m, deMath.binaryNot(deMath.shiftLeft(1, otherMantissaBits)), deMath.BinaryOp.AND);

            if (this.MantissaBits < otherMantissaBits) {
                // Round mantissa (round to nearest even).
                bitDiff = otherMantissaBits - this.MantissaBits;
                half = deMath.shiftLeft(1, (bitDiff - 1)) - 1;
                bias = deMath.binaryOp(deMath.shiftRight(m, bitDiff), 1, deMath.BinaryOp.AND);

                m = deMath.shiftRight(m + half + bias, bitDiff);

                if (deMath.binaryOp(m, deMath.shiftLeft(1, this.MantissaBits), deMath.BinaryOp.AND)) {
                    // Overflow in mantissa.
                    m = 0;
                    e += 1;
                }
            } else {
                bitDiff = this.MantissaBits - otherMantissaBits;
                m = deMath.shiftLeft(m, bitDiff);
            }

            if (e > eMax) {
                // Overflow.
                return this.inf(other.sign());
            } else {
                DE_ASSERT(deMath.deInRange32(e, eMin, eMax));
                DE_ASSERT(deMath.binaryOp((e + this.ExponentBias), deMath.binaryNot(deMath.shiftLeft(1, this.ExponentBits) - 1), deMath.BinaryOp.AND) == 0);
                DE_ASSERT(deMath.binaryOp(m, deMath.binaryNot(deMath.shiftLeft(1, this.MantissaBits) - 1), deMath.BinaryOp.AND) == 0);

                return tcuFloat.newDeFloatFromParameters(
                    deMath.binaryOp(
                        deMath.binaryOp(
                            s,
                            deMath.shiftLeft(
                                e + this.ExponentBias,
                                this.MantissaBits
                            ),
                            deMath.BinaryOp.OR
                        ),
                        m,
                        deMath.BinaryOp.OR
                    ),
                    this
                );
            }
        }
    }
};

/**
 * tcuFloat.deFloat class - Empty constructor, builds a 32 bit float by default
 * @constructor
 */
tcuFloat.deFloat = function() {
    this.description = tcuFloat.description32;

    this.m_buffer = null;
    this.m_array = null;
    this.m_array32 = null;
    this.bitValue = undefined;
    this.signValue = undefined;
    this.expValue = undefined;
    this.mantissaValue = undefined;

    this.m_value = 0;
};

/**
 * buffer - Get the deFloat's existing ArrayBuffer or create one if none exists.
 * @return {ArrayBuffer}
 */
tcuFloat.deFloat.prototype.buffer = function() {
    if (!this.m_buffer)
        this.m_buffer = new ArrayBuffer(this.description.totalByteSize);
    return this.m_buffer;
};

/**
 * array - Get the deFloat's existing Uint8Array or create one if none exists.
 * @return {Uint8Array}
 */
tcuFloat.deFloat.prototype.array = function() {
    if (!this.m_array)
        this.m_array = new Uint8Array(this.buffer());
    return this.m_array;
};

/**
 * array32 - Get the deFloat's existing Uint32Array or create one if none exists.
 * @return {Uint32Array}
 */
tcuFloat.deFloat.prototype.array32 = function() {
    if (!this.m_array32)
        this.m_array32 = new Uint32Array(this.buffer());
    return this.m_array32;
};

/**
 * deFloatNumber - To be used immediately after constructor
 * Builds a 32-bit tcuFloat.deFloat based on a 64-bit JS number.
 * @param {number} jsnumber
 * @return {tcuFloat.deFloat}
 */
tcuFloat.deFloat.prototype.deFloatNumber = function(jsnumber) {
    var view32 = new DataView(this.buffer());
    view32.setFloat32(0, jsnumber, true); //little-endian
    this.m_value = view32.getFloat32(0, true); //little-endian

    // Clear cached values
    this.bitValue = undefined;
    this.signValue = undefined;
    this.expValue = undefined;
    this.mantissaValue = undefined;

    return this;
};

/**
 * deFloatNumber64 - To be used immediately after constructor
 * Builds a 64-bit tcuFloat.deFloat based on a 64-bit JS number.
 * @param {number} jsnumber
 * @return {tcuFloat.deFloat}
 */
tcuFloat.deFloat.prototype.deFloatNumber64 = function(jsnumber) {
    var view64 = new DataView(this.buffer());
    view64.setFloat64(0, jsnumber, true); //little-endian
    this.m_value = view64.getFloat64(0, true); //little-endian

    // Clear cached values
    this.bitValue = undefined;
    this.signValue = undefined;
    this.expValue = undefined;
    this.mantissaValue = undefined;

    return this;
};

/**
 * Convenience function to build a 32-bit tcuFloat.deFloat based on a 64-bit JS number
 * Builds a 32-bit tcuFloat.deFloat based on a 64-bit JS number.
 * @param {number} jsnumber
 * @return {tcuFloat.deFloat}
 */
tcuFloat.newDeFloatFromNumber = function(jsnumber) {
    return new tcuFloat.deFloat().deFloatNumber(jsnumber);
};

/**
 * deFloatBuffer - To be used immediately after constructor
 * Builds a tcuFloat.deFloat based on a buffer and a format description.
 * The buffer is assumed to contain data of the given description.
 * @param {ArrayBuffer} buffer
 * @param {tcuFloat.FloatDescription} description
 * @return {tcuFloat.deFloat}
 */
tcuFloat.deFloat.prototype.deFloatBuffer = function(buffer, description) {
    this.m_buffer = buffer;
    this.m_array = new Uint8Array(this.m_buffer);
    this.m_array32 = new Uint32Array(this.m_buffer);

    this.m_value = deMath.arrayToNumber(this.m_array);

    // Clear cached values
    this.bitValue = undefined;
    this.signValue = undefined;
    this.expValue = undefined;
    this.mantissaValue = undefined;

    return this;
};

/**
 * Convenience function to build a tcuFloat.deFloat based on a buffer and a format description
 * The buffer is assumed to contain data of the given description.
 * @param {ArrayBuffer} buffer
 * @param {tcuFloat.FloatDescription} description
 * @return {tcuFloat.deFloat}
 */
tcuFloat.newDeFloatFromBuffer = function(buffer, description) {
    return new tcuFloat.deFloat().deFloatBuffer(buffer, description);
};

/**
 * Set the tcuFloat.deFloat from the given bitwise representation.
 *
 * @param {number} jsnumber  This is taken to be the bitwise representation of
 *   the floating point number represented by this deFloat. It must be an
 *   integer, less than 2^52, and compatible with the existing description.
 * @return {tcuFloat.deFloat}
 **/
tcuFloat.deFloat.prototype.deFloatParametersNumber = function(jsnumber) {
    /** @type {number} */ var jsnumberMax = -1 >>> (32 - this.description.totalBitSize);
    DE_ASSERT(Number.isInteger(jsnumber) && jsnumber <= jsnumberMax);

    this.m_value = jsnumber;
    deMath.numberToArray(this.m_array, jsnumber);

    // Clear cached values
    this.bitValue = undefined;
    this.signValue = undefined;
    this.expValue = undefined;
    this.mantissaValue = undefined;

    return this;
};

/**
 * Initializes a tcuFloat.deFloat from the given bitwise representation,
 * with the specified format description.
 *
 * @param {number} jsnumber  This is taken to be the bitwise representation of
 *   the floating point number represented by this deFloat. It must be an
 *   integer, less than 2^52, and compatible with the new description.
 * @param {tcuFloat.FloatDescription} description
 * @return {tcuFloat.deFloat}
 **/
tcuFloat.deFloat.prototype.deFloatParameters = function(jsnumber, description) {
    /** @type {number} */ var maxUint52 = 0x10000000000000;
    DE_ASSERT(Number.isInteger(jsnumber) && jsnumber < maxUint52);
    if (description.totalBitSize > 52) {
        // The jsnumber representation for this number can't possibly be valid.
        // Make sure it has a sentinel 0 value.
        DE_ASSERT(jsnumber === 0);
    }

    this.description = description;

    this.m_buffer = new ArrayBuffer(this.description.totalByteSize);
    this.m_array = new Uint8Array(this.m_buffer);

    return this.deFloatParametersNumber(jsnumber);
};

/**
 * Convenience function. Creates a tcuFloat.deFloat, then initializes it from
 * the given bitwise representation, with the specified format description.
 *
 * @param {number} jsnumber  This is taken to be the bitwise representation of
 *   the floating point number represented by this deFloat. It must be an
 *   integer, less than 2^52, and compatible with the new description.
 * @param {tcuFloat.FloatDescription} description
 * @return {tcuFloat.deFloat}
 **/
tcuFloat.newDeFloatFromParameters = function(jsnumber, description) {
    return new tcuFloat.deFloat().deFloatParameters(jsnumber, description);
};

/**
 * Returns bit range [begin, end)
 * @param {number} begin
 * @param {number} end
 * @return {number}
 */
tcuFloat.deFloat.prototype.getBitRange = function(begin, end) {
    if (this.description.totalBitSize <= 52) {
        // this.bits() is invalid for more than 52 bits.
        return deMath.getBitRange(this.bits(), begin, end);
    } else {
        return deMath.getArray32BitRange(this.array32(), begin, end);
    }
};

/**
 * Returns the raw binary representation value of the tcuFloat.deFloat
 * @return {number}
 */
tcuFloat.deFloat.prototype.bits = function() {
    if (typeof this.bitValue === 'undefined')
        this.bitValue = deMath.arrayToNumber(this.array());
    return this.bitValue;
};

/**
 * Returns the raw binary sign bit
 * @return {number}
 */
tcuFloat.deFloat.prototype.signBit = function() {
    if (typeof this.signValue === 'undefined')
        this.signValue = this.getBitRange(this.description.totalBitSize - 1, this.description.totalBitSize);
    return this.signValue;
};

/**
 * Returns the raw binary exponent bits
 * @return {number}
 */
tcuFloat.deFloat.prototype.exponentBits = function() {
    if (typeof this.expValue === 'undefined')
        this.expValue = this.getBitRange(this.description.MantissaBits, this.description.MantissaBits + this.description.ExponentBits);
    return this.expValue;
};

/**
 * Returns the raw binary mantissa bits
 * @return {number}
 */
tcuFloat.deFloat.prototype.mantissaBits = function() {
    if (typeof this.mantissaValue === 'undefined')
        this.mantissaValue = this.getBitRange(0, this.description.MantissaBits);
    return this.mantissaValue;
};

/**
 * Returns the sign as a factor (-1 or 1)
 * @return {number}
 */
tcuFloat.deFloat.prototype.sign = function() {
    var sign = this.signBit();
    var signvalue = sign ? -1 : 1;
    return signvalue;
};

/**
 * Returns the real exponent, checking if it's a denorm or zero number or not
 * @return {number}
 */
tcuFloat.deFloat.prototype.exponent = function() {return this.isDenorm() ? 1 - this.description.ExponentBias : this.exponentBits() - this.description.ExponentBias;};

/**
 * Returns the (still raw) mantissa, checking if it's a denorm or zero number or not
 * Makes the normally implicit bit explicit.
 * @return {number}
 */
tcuFloat.deFloat.prototype.mantissa = function() {return this.isZero() || this.isDenorm() ? this.mantissaBits() : deMath.binaryOp(this.mantissaBits(), deMath.shiftLeft(1, this.description.MantissaBits), deMath.BinaryOp.OR);};

/**
 * Returns if the number is infinity or not.
 * @return {boolean}
 */
tcuFloat.deFloat.prototype.isInf = function() {return this.exponentBits() == ((1 << this.description.ExponentBits) - 1) && this.mantissaBits() == 0;};

/**
 * Returns if the number is NaN or not.
 * @return {boolean}
 */
tcuFloat.deFloat.prototype.isNaN = function() {return this.exponentBits() == ((1 << this.description.ExponentBits) - 1) && this.mantissaBits() != 0;};

/**
 * Returns if the number is zero or not.
 * @return {boolean}
 */
tcuFloat.deFloat.prototype.isZero = function() {return this.exponentBits() == 0 && this.mantissaBits() == 0;};

/**
 * Returns if the number is denormalized or not.
 * @return {boolean}
 */
tcuFloat.deFloat.prototype.isDenorm = function() {return this.exponentBits() == 0 && this.mantissaBits() != 0;};

/**
 * Builds a zero float of the current binary description.
 * @param {number} sign
 * @return {tcuFloat.deFloat}
 */
tcuFloat.deFloat.prototype.zero = function(sign) {
    return this.description.zero(sign);
};

/**
 * Builds an infinity float representation of the current binary description.
 * @param {number} sign
 * @return {tcuFloat.deFloat}
 */
tcuFloat.deFloat.prototype.inf = function(sign) {
    return this.description.inf(sign);
};

/**
 * Builds a NaN float representation of the current binary description.
 * @return {tcuFloat.deFloat}
 */
tcuFloat.deFloat.prototype.nan = function() {
    return this.description.nan();
};

/**
 * Builds a float of the current binary description.
 * Given a sign, exponent and mantissa.
 * @param {number} sign
 * @param {number} exponent
 * @param {number} mantissa
 * @return {tcuFloat.deFloat}
 */
tcuFloat.deFloat.prototype.construct = function(sign, exponent, mantissa) {
    return this.description.construct(sign, exponent, mantissa);
};

/**
 * Builds a float of the current binary description.
 * Given a sign, exponent and a raw binary mantissa.
 * @param {number} sign
 * @param {number} exponent
 * @param {number} mantissaBits Raw binary mantissa.
 * @return {tcuFloat.deFloat}
 */
tcuFloat.deFloat.prototype.constructBits = function(sign, exponent, mantissaBits) {
    return this.description.constructBits(sign, exponent, mantissaBits);
};

/**
 * Calculates the JS float number from the internal representation.
 * @return {number} The JS float value represented by this tcuFloat.deFloat.
 */
tcuFloat.deFloat.prototype.getValue = function() {
    if ((this.description.Flags | tcuFloat.FloatFlags.FLOAT_HAS_SIGN) === 0 && this.sign() < 0)
        return 0;
    if (this.isInf())
        return Number.Infinity;
    if (this.isNaN())
        return Number.NaN;
    if (this.isZero())
        return this.sign() * 0;
    /**@type {number} */ var mymantissa = this.mantissa();
    /**@type {number} */ var myexponent = this.exponent();
    /**@type {number} */ var sign = this.sign();

    /**@type {number} */ var value = mymantissa / Math.pow(2, this.description.MantissaBits) * Math.pow(2, myexponent);

    if (this.description.Flags | tcuFloat.FloatFlags.FLOAT_HAS_SIGN != 0)
        value = value * sign;

    return value;
};

tcuFloat.description10 = new tcuFloat.FloatDescription(5, 5, 15, 0);
tcuFloat.description11 = new tcuFloat.FloatDescription(5, 6, 15, 0);
tcuFloat.description16 = new tcuFloat.FloatDescription(5, 10, 15, tcuFloat.FloatFlags.FLOAT_HAS_SIGN);
tcuFloat.description32 = new tcuFloat.FloatDescription(8, 23, 127, tcuFloat.FloatFlags.FLOAT_HAS_SIGN | tcuFloat.FloatFlags.FLOAT_SUPPORT_DENORM);
tcuFloat.description64 = new tcuFloat.FloatDescription(11, 52, 1023, tcuFloat.FloatFlags.FLOAT_HAS_SIGN | tcuFloat.FloatFlags.FLOAT_SUPPORT_DENORM);

tcuFloat.convertFloat32Inline = (function() {
    var float32View = new Float32Array(1);
    var int32View = new Int32Array(float32View.buffer);

    return function(fval, description) {
        float32View[0] = fval;
        var fbits = int32View[0];

        var exponentBits = (fbits >> 23) & 0xff;
        var mantissaBits = fbits & 0x7fffff;
        var signBit = (fbits & 0x80000000) ? 1 : 0;
        var sign = signBit ? -1 : 1;

        var isZero = exponentBits == 0 && mantissaBits == 0;

        var bitDiff;
        var half;
        var bias;

        if (!(description.Flags & tcuFloat.FloatFlags.FLOAT_HAS_SIGN) && sign < 0) {
            // Negative number, truncate to zero.
            return description.zeroNumber(+1);
        } else if (exponentBits == ((1 << tcuFloat.description32.ExponentBits) - 1) && mantissaBits == 0) { // isInf
            return description.infNumber(sign);
        } else if (exponentBits == ((1 << tcuFloat.description32.ExponentBits) - 1) && mantissaBits != 0) { // isNaN
            return description.nanNumber();
        } else if (isZero) {
            return description.zeroNumber(sign);
        } else {
            var eMin = 1 - description.ExponentBias;
            var eMax = ((1 << description.ExponentBits) - 2) - description.ExponentBias;

            var isDenorm = exponentBits == 0 && mantissaBits != 0;

            var s = signBit << (description.ExponentBits + description.MantissaBits); // \note Not sign, but sign bit.
            var e = isDenorm ? 1 - tcuFloat.description32.ExponentBias : exponentBits - tcuFloat.description32.ExponentBias;// other.exponent();
            var m = isZero || isDenorm ? mantissaBits : mantissaBits | (1 << tcuFloat.description32.MantissaBits); // other.mantissa();

            // Normalize denormalized values prior to conversion.
            while (!(m & (1 << tcuFloat.description32.MantissaBits))) {
                m = deMath.shiftLeft(m, 1);
                e -= 1;
            }

            if (e < eMin) {
                // Underflow.
                if ((description.Flags & tcuFloat.FloatFlags.FLOAT_SUPPORT_DENORM) && (eMin - e - 1 <= description.MantissaBits)) {
                    // Shift and round (RTE).
                    bitDiff = (tcuFloat.description32.MantissaBits - description.MantissaBits) + (eMin - e);
                    half = (1 << (bitDiff - 1)) - 1;
                    bias = ((m >> bitDiff) & 1);
                    return (s | ((m + half + bias) >> bitDiff));
                } else
                    return description.zeroNumber(sign);
            } else {
                // Remove leading 1.
                m = (m & ~(1 << tcuFloat.description32.MantissaBits));

                if (description.MantissaBits < tcuFloat.description32.MantissaBits) {
                    // Round mantissa (round to nearest even).
                    bitDiff = tcuFloat.description32.MantissaBits - description.MantissaBits;
                    half = (1 << (bitDiff - 1)) - 1;
                    bias = ((m >> bitDiff) & 1);

                    m = (m + half + bias) >> bitDiff;

                    if ((m & (1 << description.MantissaBits))) {
                        // Overflow in mantissa.
                        m = 0;
                        e += 1;
                    }
                } else {
                    bitDiff = description.MantissaBits - tcuFloat.description32.MantissaBits;
                    m = (m << bitDiff);
                }

                if (e > eMax) {
                    // Overflow.
                    return description.infNumber(sign);
                } else {
                    DE_ASSERT(deMath.deInRange32(e, eMin, eMax));
                    DE_ASSERT(((e + description.ExponentBias) & ~((1 << description.ExponentBits) - 1)) == 0);
                    DE_ASSERT((m & ~((1 << description.MantissaBits) - 1)) == 0);

                    return (s | ((e + description.ExponentBias) << description.MantissaBits)) | m;
                }
            }
        }
    };
})();

/**
 * Builds a 10 bit tcuFloat.deFloat
 * @param {number} value (64-bit JS float)
 * @return {tcuFloat.deFloat}
 */
tcuFloat.newFloat10 = function(value) {
    DE_ASSERT(Number.isInteger(value) && value <= 0x3ff);
    /**@type {tcuFloat.deFloat} */ var other32 = new tcuFloat.deFloat().deFloatNumber(value);
    return tcuFloat.description10.convert(other32);
};

/**
 * Builds a 11 bit tcuFloat.deFloat
 * @param {number} value (64-bit JS float)
 * @return {tcuFloat.deFloat}
 */
tcuFloat.newFloat11 = function(value) {
    /**@type {tcuFloat.deFloat} */ var other32 = new tcuFloat.deFloat().deFloatNumber(value);
    return tcuFloat.description11.convert(other32);
};

/**
 * Builds a 16 bit tcuFloat.deFloat
 * @param {number} value (64-bit JS float)
 * @return {tcuFloat.deFloat}
 */
tcuFloat.newFloat16 = function(value) {
    /**@type {tcuFloat.deFloat} */ var other32 = new tcuFloat.deFloat().deFloatNumber(value);
    return tcuFloat.description16.convert(other32);
};

/**
 * Builds a 16 bit tcuFloat.deFloat from raw bits
 * @param {number} value (16-bit value)
 * @return {tcuFloat.deFloat}
 */
tcuFloat.newFloat32From16 = function(value) {
    var other16 = tcuFloat.newDeFloatFromParameters(value, tcuFloat.description16);
    return tcuFloat.description32.convert(other16);
};

/**
 * Builds a 16 bit tcuFloat.deFloat with no denorm support
 * @param {number} value (64-bit JS float)
 * @return {tcuFloat.deFloat}
 */
tcuFloat.newFloat16NoDenorm = function(value) {
    /**@type {tcuFloat.deFloat} */ var other32 = new tcuFloat.deFloat().deFloatNumber(value);
    return tcuFloat.description16.convert(other32);
};

/**
 * Builds a 32 bit tcuFloat.deFloat
 * @param {number} value (64-bit JS float)
 * @return {tcuFloat.deFloat}
 */
tcuFloat.newFloat32 = function(value) {
    return new tcuFloat.deFloat().deFloatNumber(value);
};

tcuFloat.numberToFloat11 = function(value) {
    return tcuFloat.convertFloat32Inline(value, tcuFloat.description11);
};

tcuFloat.float11ToNumber = (function() {
    var x = tcuFloat.newDeFloatFromParameters(0, tcuFloat.description11);
    return function(float11) {
        x.deFloatParametersNumber(float11);
        return x.getValue();
    };
})();

tcuFloat.numberToFloat10 = function(value) {
    return tcuFloat.convertFloat32Inline(value, tcuFloat.description10);
};

tcuFloat.float10ToNumber = (function() {
    var x = tcuFloat.newDeFloatFromParameters(0, tcuFloat.description10);
    return function(float10) {
        x.deFloatParametersNumber(float10);
        return x.getValue();
    };
})();

tcuFloat.numberToHalfFloat = function(value) {
    return tcuFloat.convertFloat32Inline(value, tcuFloat.description16);
};

tcuFloat.numberToHalfFloatNoDenorm = function(value) {
    return tcuFloat.newFloat16NoDenorm(value).bits();
};

tcuFloat.halfFloatToNumber = (function() {
    var x = tcuFloat.newDeFloatFromParameters(0, tcuFloat.description16);
    return function(half) {
        x.deFloatParametersNumber(half);
        return x.getValue();
    };
})();

tcuFloat.halfFloatToNumberNoDenorm = tcuFloat.halfFloatToNumber;

/**
 * Builds a 64 bit tcuFloat.deFloat
 * @param {number} value (64-bit JS float)
 * @return {tcuFloat.deFloat}
 */
tcuFloat.newFloat64 = function(value) {
    return new tcuFloat.deFloat().deFloatParameters(0, tcuFloat.description64)
        .deFloatNumber64(value);
};

});
