time-to-botec

Benchmark sampling in different programming languages
Log | Files | Refs | README

roundn.js (5992B)


      1 /**
      2 * @license Apache-2.0
      3 *
      4 * Copyright (c) 2018 The Stdlib Authors.
      5 *
      6 * Licensed under the Apache License, Version 2.0 (the "License");
      7 * you may not use this file except in compliance with the License.
      8 * You may obtain a copy of the License at
      9 *
     10 *    http://www.apache.org/licenses/LICENSE-2.0
     11 *
     12 * Unless required by applicable law or agreed to in writing, software
     13 * distributed under the License is distributed on an "AS IS" BASIS,
     14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     15 * See the License for the specific language governing permissions and
     16 * limitations under the License.
     17 */
     18 
     19 'use strict';
     20 
     21 // MODULES //
     22 
     23 var isnan = require( './../../../../base/assert/is-nan' );
     24 var isInfinite = require( './../../../../base/assert/is-infinite' );
     25 var pow = require( './../../../../base/special/pow' );
     26 var abs = require( './../../../../base/special/abs' );
     27 var round = require( './../../../../base/special/round' );
     28 var MAX_SAFE_INTEGER = require( '@stdlib/constants/float64/max-safe-integer' );
     29 var MAX_EXP = require( '@stdlib/constants/float64/max-base10-exponent' );
     30 var MIN_EXP = require( '@stdlib/constants/float64/min-base10-exponent' );
     31 var MIN_EXP_SUBNORMAL = require( '@stdlib/constants/float64/min-base10-exponent-subnormal' );
     32 
     33 
     34 // VARIABLES //
     35 
     36 var MAX_INT = MAX_SAFE_INTEGER + 1;
     37 var HUGE = 1.0e+308;
     38 
     39 
     40 // MAIN //
     41 
     42 /**
     43 * Rounds a numeric value to the nearest multiple of \\(10^n\\).
     44 *
     45 * ## Method
     46 *
     47 * 1.  If \\(|x| <= 2^{53}\\) and \\(|n| <= 308\\), we can use the formula
     48 *
     49 *     ```tex
     50 *     \operatorname{roundn}(x,n) = \frac{\operatorname{round}(x \cdot 10^{-n})}{10^{-n}}
     51 *     ```
     52 *
     53 *     which shifts the decimal to the nearest multiple of \\(10^n\\), performs a standard \\(\mathrm{round}\\) operation, and then shifts the decimal to its original position.
     54 *
     55 *     <!-- <note> -->
     56 *
     57 *     If \\(x \cdot 10^{-n}\\) overflows, \\(x\\) lacks a sufficient number of decimal digits to have any effect when rounding. Accordingly, the rounded value is \\(x\\).
     58 *
     59 *     <!-- </note> -->
     60 *
     61 *     <!-- <note> -->
     62 *
     63 *     Note that rescaling \\(x\\) can result in unexpected behavior. For instance, the result of \\(\operatorname{roundn}(0.2+0.1,-16)\\) is \\(0.3000000000000001\\) and not \\(0.3\\). While possibly unexpected, this is not a bug. The behavior stems from the fact that most decimal fractions cannot be exactly represented as floating-point numbers. And further, rescaling can lead to slightly different fractional values, which, in turn, affects the result of \\(\mathrm{round}\\).
     64 *
     65 *     <!-- </note> -->
     66 *
     67 * 2.  If \\(n > 308\\), we recognize that the maximum absolute double-precision floating-point number is \\(\approx 1.8\mbox{e}308\\) and, thus, the result of rounding any possible finite number \\(x\\) to the nearest \\(10^n\\) is \\(0.0\\). To ensure consistent behavior with \\(\operatorname{round}(x)\\), the sign of \\(x\\) is preserved.
     68 *
     69 * 3.  If \\(n < -324\\), \\(n\\) exceeds the maximum number of possible decimal places (such as with subnormal numbers), and, thus, the rounded value is \\(x\\).
     70 *
     71 * 4.  If \\(x > 2^{53}\\), \\(x\\) is **always** an integer (i.e., \\(x\\) has no decimal digits). If \\(n <= 0\\), the rounded value is \\(x\\).
     72 *
     73 * 5.  If \\(n < -308\\), we let \\(m = n + 308\\) and modify the above formula to avoid overflow.
     74 *
     75 *     ```tex
     76 *     \operatorname{roundn}(x,n) = \frac{\biggl(\frac{\operatorname{round}( (x \cdot 10^{308}) 10^{-m})}{10^{308}}\biggr)}{10^{-m}}
     77 *     ```
     78 *
     79 *     If overflow occurs, the rounded value is \\(x\\).
     80 *
     81 *
     82 * ## Special Cases
     83 *
     84 * ```tex
     85 * \begin{align*}
     86 * \operatorname{roundn}(\mathrm{NaN}, n) &= \mathrm{NaN} \\
     87 * \operatorname{roundn}(x, \mathrm{NaN}) &= \mathrm{NaN} \\
     88 * \operatorname{roundn}(x, \pm\infty) &= \mathrm{NaN} \\
     89 * \operatorname{roundn}(\pm\infty, n) &= \pm\infty \\
     90 * \operatorname{roundn}(\pm 0, n) &= \pm 0
     91 * \end{align*}
     92 * ```
     93 *
     94 * ## Notes
     95 *
     96 * 1.  Alternative algorithms:
     97 *
     98 *     -   Round by [casting][1] \\(x\\) to an exponential string.
     99 *     -   Native Python implementation [1][2] and [2][3].
    100 *
    101 * [1]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round
    102 * [2]: https://hg.python.org/releasing/2.7.9/file/tip/Objects/floatobject.c#l1082
    103 * [3]: https://hg.python.org/releasing/2.7.9/file/tip/Objects/floatobject.c#l1226
    104 *
    105 *
    106 * @param {number} x - input value
    107 * @param {integer} n - integer power of `10`
    108 * @returns {number} rounded value
    109 *
    110 * @example
    111 * // Round a value to 2 decimal places:
    112 * var v = roundn( 3.141592653589793, -2 );
    113 * // returns 3.14
    114 *
    115 * @example
    116 * // If n = 0, `roundn` behaves like `round`:
    117 * var v = roundn( 3.141592653589793, 0 );
    118 * // returns 3.0
    119 *
    120 * @example
    121 * // Round a value to the nearest thousand:
    122 * var v = roundn( 12368.0, 3 );
    123 * // returns 12000.0
    124 */
    125 function roundn( x, n ) {
    126 	var s;
    127 	var y;
    128 	if (
    129 		isnan( x ) ||
    130 		isnan( n ) ||
    131 		isInfinite( n )
    132 	) {
    133 		return NaN;
    134 	}
    135 	if (
    136 		// Handle infinities...
    137 		isInfinite( x ) ||
    138 
    139 		// Handle +-0...
    140 		x === 0.0 ||
    141 
    142 		// If `n` exceeds the maximum number of feasible decimal places (such as with subnormal numbers), nothing to round...
    143 		n < MIN_EXP_SUBNORMAL ||
    144 
    145 		// If `|x|` is large enough, no decimals to round...
    146 		( abs( x ) > MAX_INT && n <= 0 )
    147 	) {
    148 		return x;
    149 	}
    150 	// The maximum absolute double is ~1.8e308. Accordingly, any possible finite `x` rounded to the nearest >=10^309 is 0.0.
    151 	if ( n > MAX_EXP ) {
    152 		return 0.0 * x; // preserve the sign (same behavior as round)
    153 	}
    154 	// If we overflow, return `x`, as the number of digits to the right of the decimal is too small (i.e., `x` is too large / lacks sufficient fractional precision) for there to be any effect when rounding...
    155 	if ( n < MIN_EXP ) {
    156 		s = pow( 10.0, -(n + MAX_EXP) );
    157 		y = (x*HUGE) * s; // order of operation matters!
    158 		if ( isInfinite( y ) ) {
    159 			return x;
    160 		}
    161 		return ( round(y)/HUGE ) / s;
    162 	}
    163 	s = pow( 10.0, -n );
    164 	y = x * s;
    165 	if ( isInfinite( y ) ) {
    166 		return x;
    167 	}
    168 	return round( y ) / s;
    169 }
    170 
    171 
    172 // EXPORTS //
    173 
    174 module.exports = roundn;