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 });