📜 ⬆️ ⬇️

Math in javascript


Introduction


Having numbers and numeric data allows you to work with the language in some way. But besides working with arithmetic operators in JavaScript, mathematical constructs can be a daunting task for beginners. For this reason, it’s necessary to concentrate not on syntax, but on general mathematical functions, this list starts with such things as sorting, rounding and generating random values, this is enough before going into details. To work with math in JavaScript, you only need to know about things like function, operand, and operator.

From translators


Hello everyone, Maxim Ivanov and Dmitry Sergienkov are with you, and today we decided that we will not talk about fashionable and useful things like ReactJS, Angular, TypeScript and others. Today we will focus on math in javascript. If you like math , you can do it all your free time, but if your goal is not scientific research, but work as a programmer, mathematics is unlikely to become the best object to learn .

  1. Work with random numbers


  2. Rounding
    ')

  3. Sorting


  4. Work with power functions


  5. Mathematical constants


  6. Math.abs, parseInt, parseFloat



Work with random numbers


Random numbers are often required in JavaScript, for example, to draw stars scattered across the night sky. But there are many different types of accidents, and depending on the logic and needs of your application, you may need one of them.

Main case


The simplest form of randomness is the Math.random () function built into JavaScript.

> Math.random() 0.19401081069372594 

Math.random () always returns a floating-point number between 0 and 1. From a technical point of view, the number returned with Math.random () may be 0, but it will never be 1.

If you use Math.random () often, use your own function in scripts:

 function getRandom() { return Math.random(); } 

The problem, of course, is that this function will always create a random number within a very limited range, then we will try to consider some recommendations designed to solve this problem.

Random number in the interval [min, max)


Extending this functionality requires a bit of math:

Floating point number:

 function getRandomFloat(min, max) { return Math.random() * (max - min) + min; } getRandomFloat(11, 101) > 75.31898734299466 

Integer random number:

 function getRandomInt(min, max) { return Math.floor(Math.random() * (max - min)) + min; } getRandomInt(10, 20) > 12 

Random number in the interval [min, max]


 function getRandomInRange(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; } getRandomInRange(1, 10) > 7 

Boolean random variables (true / false)


If you want a simple randomness of 0 or 1, representing the coin flip:

 function coinToss() { return Math.floor(Math.random() * 2); } coinToss(); > 0 

If you need to get true or false:

 function coinToss() { return (Math.floor(Math.random() * 2) === 0); } coinToss(); > true 

Or:

 function coinToss() { return Math.random()<.5; } coinToss(); > true 

If you want to associate specific words with the sides of a coin (yes / no, top / bottom, etc.):

 function coinFlip() { return (Math.floor(Math.random() * 2) === 0) ? "up" : "down"; } coinFlip(); > up 

Random Variables with Exceptions


For a limited range of integers, you need to create an array of numbers that you would like to extract and then select randomly from this array:

 let numPool = [ 1, 3, 5, 7, 9, 10 ], rand = numPool[Math.floor(Math.random() * numPool.length)]; 

You can also use an array of numbers that you want to exclude, and prepare an empty array, which will contain the result of filtering from the first array to the second:

 let numPool = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; let excludePool = [ 3, 4 ]; let filteredPool = []; 

Then, in the loop, we run the numPool array, if the returned number exists and is in the excludePool array, put the result in the filteredPool:

 for (let i = 0; i < numPool.length; i++) { if (excludePool.indexOf(numPool[i]) === -1) { filteredPool.push(numPool[i]); } } 

And finally, we get random numbers from the filteredPool array:

 let rand = filteredPool[Math.floor(Math.random() * filteredPool.length)]; 

Random values ​​without repetitions


For a small set of numbers, you need to create an array filled with elements, then shuffle them in a random order, place the result in a new array, and then pull out one by one:

 let numPool = [ 13, 21, 36, 14, 27, 10 ]; function shuffle(numPool) { for( let j, x, i = numPool.length; i; j = parseInt(Math.random() * i), x = numPool[--i], numPool[i] = numPool[j], numPool[j] = x ); return numPool; }; let randomResult = shuffle(numPool); while( randomResult.length > 0 ) { console.log( randomResult.pop() ); } 

To obtain a wider range of numbers, create and fill in an array of random numbers, excluding any that were previously generated:

 let numReserve = [] while (numReserve.length < 12) { let randomNumber = Math.ceil(Math.random() * 1000); let found = false; for (let i = 0; i < numReserve.length; i++) { if (numReserve[i] === randomNumber){ found = true; break; } } if (!found) { numReserve[numReserve.length]=randomNumber; } } 

In the above code, numReserve is filled with 12 random numbers from 0 to 1000.

Cryptographic random variables


Unfortunately, none of the above methods create a number with sufficient randomness for cryptographically protected functions (Math.random () is not a sufficient function that generates random numbers). Therefore, we can use the Web Cryptography API by creating a typedArray:

 let cryptoStor = new Uint16Array(8); 

In this case, we create an array with eight different slots, each of which contains an unsigned 16-bit integer. There are other variants of Int8Array, Uint8Array, int16Array, Int32Array and Uint32Array.

Then, you need to fill the array with random numbers of a certain type:

 window.crypto.getRandomValues(cryptoStor); > [43484, 57947, 46691, 49849, 24272, 11827, 28203, 17423] 

Everything else, Web Cryptography API has good support in modern browsers .

To read:


1. Random prime number
2. Generate random integers in javascript in a specific range
3. Introduction to cryptography
4. Cryptographically Pseudorandom Number Generator
5. Pseudo Random Number Generator

Rounding


Very often, calculations in JavaScript do not exactly give the results we want. Of course, we can do anything with numbers - round up or down, set ranges, cut off unnecessary numbers to a certain number of decimal places, it all depends on what you want to do in the future with this number.

Why rounding is needed?


One curious aspect of JavaScript is that it does not actually store integers; we immediately work with floating point numbers. This, combined with the fact that many fractional values ​​cannot be expressed by a finite number of decimal places, we can get the following results in JavaScript:

 0.1 * 0.2; > 0.020000000000000004 0.3 - 0.1 > 0.19999999999999998 

For practical purposes, this inaccuracy is irrelevant; in our case, we are talking about an error in quintillion fractions, however, this may disappoint someone. We can get a somewhat strange result when working with numbers that represent currency, interest or file size values. In order to correct these inaccuracies, we just need to be able to round the results, and it is enough to establish decimal accuracy.

Rounding numbers has a practical application, we can manipulate a number in a certain range, for example, we want to round the value to the nearest whole number, and not to work only with the decimal part.

Decimal Rounding


To trim the decimal number, use toFixed or the toPrecision method. Both of them take a single argument that determines, respectively, how many significant digits (i.e. the total number of digits used in a number) or decimal places (the number after the decimal point) should include the result:

  1. If the argument is not defined for toFixed (), then by default it will be zero, which means 0 decimal places, the argument has a maximum value of 20.
  2. If the argument is not specified for toPrecision, the number remains intact.

 let randNum = 6.25; randNum.toFixed(); > "6" Math.PI.toPrecision(1); > "3" randNum = 87.335; randNum.toFixed(2); > "87.33" randNum = 87.337; randNum.toPrecision(3); > "87.3" 

Both toFixed () and toPrecision () return a string representation of the result, not a number. This means that when summing the rounded value with randNum, string concatenation will be performed, not the sum of numbers:

 let randNum = 6.25; let rounded = randNum.toFixed(); // "6" console.log(randNum + rounded); > "6.256" 

If you want the result to have a numeric data type, then you will need to use parseFloat:

 let randNum = 6.25; let rounded = parseFloat(randNum.toFixed(1)); console.log(rounded); > 6.3 

Note that the values ​​of 5 are rounded, except in rare cases.

The toFixed () and toPrecision () methods are useful because they can not only cut off the fractional part, but also supplement the decimal places, which is convenient when working with currency:

 let wholeNum = 1 let dollarsCents = wholeNum.toFixed(2); console.log(dollarsCents); > "1.00" 

Cost note that toPrecision will give the result in an exponential notation if the number of integers is greater than the precision itself:

 let num = 123.435 num.toPrecision(2); > "1.2e+2" 

How to avoid rounding errors with decimal numbers


In some cases, toFixed and toPrecision rounds the value of 5 down, and up:

 let numTest = 1.005; numTest.toFixed(2); > "1.00" 

The result of the calculation above should have been 1.01, not 1. If you want to avoid such an error, we can use the solution proposed by Jack L Moore , which uses exponential numbers to calculate:

 function round(value, decimals) { return Number(Math.round(value+'e'+decimals)+'e-'+decimals); } 

Now:

 round(1.005,2); > 1.01 

If you want a more reliable solution than the solution shown above, you can upgrade to MDN .

Engine Epsilon Rounding


An alternative method for rounding decimals was introduced in ES6. Machine epsilon rounding provides a reasonable margin of error when comparing two floating point numbers. Without rounding, comparisons can produce results like the following:

 0.1 + 0.2 === 0.3 > false 

We use Math.EPSILON in our function to get the correct comparison:

 function epsEqu(x, y) { return Math.abs(x - y) < Number.EPSILON * Math.max(Math.abs(x), Math.abs(y)); } 

The function takes two arguments: the first is the current calculation, the second is the expected result. It returns a comparison of the two:

 epsEqu(0.1 + 0.2, 0.3) > true 

All modern browsers already support ES6 math functions, but if you want support in browsers like IE 11, use polyfills .

Cut off the fractional part


All the methods presented above are able to round to decimal numbers. In order to simply cut the number down to two decimal places, you must first multiply it by 100, and then divide the result by 100:

 function truncated(num) { return Math.trunc(num * 100) / 100; } truncated(3.1416) > 3.14 

If you want to adjust the method to any number of decimal places, you can use double bitwise negation :

 function truncated(num, decimalPlaces) { let numPowerConverter = Math.pow(10, decimalPlaces); return ~~(num * numPowerConverter)/numPowerConverter; } 

Now:

 let randInt = 35.874993; truncated(randInt,3); > 35.874 

Round to the nearest number


To round the decimal number to the nearest number, either up or down, depending on what we are closest to, use Math.round ():

 Math.round(4.3) > 4 Math.round(4.5) > 5 

Note that the “half value”, 0.5 is rounded up according to the rules of mathematics .

Round to the nearest integer


If you want to always round down, use Math.floor:

 Math.floor(42.23); > 42 Math.floor(36.93); > 36 

Please note that rounding down works for all numbers, including negative ones. Imagine a skyscraper with an infinite number of floors, including floors of the lower level (representing negative numbers). If you are in the elevator at the lower level between 2 and 3 (which is a value of -2.5), Math.floor takes you to -3:

 Math.floor(-2.5); > -3 

But if you want to avoid this situation, use Math.trunc , supported in all modern browsers (except IE / Edge):

 Math.trunc(-41.43); > -41 

On MDN you will find polyfill, which will provide support for Math.trunc in browsers and IE / Edge.

Round up to the nearest integer


On the other hand, if you need to always round up, use Math.ceil. Again, remember the endless elevator: Math.ceil will always go "up", regardless of whether the number is negative or not:

 Math.ceil(42.23); > 43 Math.ceil(36.93); > 37 Math.ceil(-36.93); > -36 

Round to higher / lower required number


If we want to round to the nearest multiple of 5, the easiest way is to create a function that divides a number by 5, rounds it up, and then multiplies it by the same amount:

 function roundTo5(num) { return Math.round(num/5)*5; } 

Now:

 roundTo5(11); > 10 

If you want to round up to a multiple of its value, we use a more general function, passing in it the initial value and the multiple:

 function roundToMultiple(num, multiple) { return Math.round(num/multiple)*multiple; } 

Now:

 let initialNumber = 11; let multiple = 10; roundToMultiple(initialNumber, multiple); > 10; 

Fixing the number in the range


There are many cases where we want to get an x ​​value that lies within the range. For example, we may need a value from 1 to 100, but at the same time we got a value of 123. In order to fix this, we can use the minimum (returns the smallest of a set of numbers) and the maximum (returns the largest of any set of numbers). In our example, the range is from 1 to 100:

 let lowBound = 1; let highBound = 100; let numInput = 123; let clamped = Math.max(lowBound, Math.min(numInput, highBound)); console.log(clamped); > 100; 

Again, we can reuse the operation and wrap it all in a function, use the solution proposed by Daniel X. Moore :

 Number.prototype.clamp = function(min, max) { return Math.min(Math.max(this, min), max); }; 

Now:

 numInput.clamp(lowBound, highBound); > 100; 

Gaussian rounding


Gaussian rounding, also known as bank rounding, is that rounding for this case occurs to the nearest even round. This rounding method works without statistical error. The best solution was suggested by Tim Down :

 function gaussRound(num, decimalPlaces) { let d = decimalPlaces || 0, m = Math.pow(10, d), n = +(d ? num * m : num).toFixed(8), i = Math.floor(n), f = n - i, e = 1e-8, r = (f > 0.5 - e && f < 0.5 + e) ? ((i % 2 == 0) ? i : i + 1) : Math.round(n); return d ? r / m : r; } 

Now:

 gaussRound(2.5) > 2 gaussRound(3.5) > 4 gaussRound(2.57,1) > 2.6 

Decimal in CSS:

Since JavaScript is often used to create a positional transformation of HTML elements, you may wonder what happens if we generate decimal values ​​for our elements:

 #box { width: 63.667731993px; } 

The good news is that modern browsers will consider decimal values ​​in the block model, including in percentage or pixel units.

To read:


1. What should every scientist know about arithmetic when working with floating point
2. Fast JavaScript library working with arbitrary precision decimal arithmetic
3. Extended Decimal type of arbitrary precision for JavaScript
4. JavaScript library of arbitrary precision for decimal and arbitrary arithmetic
5. What every JavaScript developer should know about floating point
6. Why such problems with floating point in JS?

Sorting


Very often we have to sort out any elements, for example, we have an array of game records, and they must be organized in descending order of player rank. Unfortunately, the standard sort () method has some surprising limitations: it works well with frequently used English words, but it immediately breaks down when it encounters numbers, unique characters or words in upper case.

Sort alphabetically


It would seem that sorting an array alphabetically should be the simplest task:

 let fruit = ["butternut squash", "apricot", "cantaloupe"]; fruit.sort(); > "apricot", "butternut squash", "cantaloupe"] 

However, we run into a problem as soon as one of the elements is in upper case:

 let fruit = ["butternut squash", "apricot", "Cantalope"]; fruit.sort(); > "Cantaloupe", "apricot", "butternut squash"] 

This is due to the fact that, by default, the sorter compares the first character represented in Unicode . Unicode is a unique code for any character, regardless of the platform, regardless of the program, regardless of the language. For example, if you look at the code table, the character “a” has the value U + 0061 (in the hexadecimal system 0x61 ), while the character “C” has the code U + 0043 (0x43), which comes earlier in the Unicode table than the character "A".

To sort an array that can contain mixed first-letter registers, we need to either convert all elements temporarily to lower case, or define our own sorting order using the localeCompare () method with some arguments. As a rule, for such a case, it is better to immediately create a function for reuse:

 function alphaSort(arr) { arr.sort(function (a, b) { return a.localeCompare(b, 'en', {'sensitivity': 'base'}); }); } let fruit = ["butternut squash", "apricot", "Cantaloupe"]; alphaSort(fruit) > ["apricot", "butternut squash", "Cantaloupe"] 

If you want to get an array sorted in reverse alphabetical order, simply change the positions of a and b in the function:

 function alphaSort(arr) { arr.sort(function (a, b) { return b.localeCompare(a, 'en', {'sensitivity': 'base'}); }); } let fruit = ["butternut squash", "apricot", "Cantaloupe"]; alphaSort(fruit) > ["Cantaloupe", "butternut squash", "apricot"] 

Here it is worth noting that localeCompare is used with arguments, we also have to remember that it is supported by IE11 +, for older versions of IE, we can use it without arguments, and in lower case:

 function caseSort(arr) { arr.sort(function (a, b) { return a.toLowerCase().localeCompare(b.toLowerCase()); }); } let fruit = ["butternut squash", "apricot", "Cantaloupe"]; caseSort(fruit) > ["apricot", "butternut squash", "Cantaloupe"] 

Numeric sorting


All this does not apply to the example of which we spoke above about the array of game records. With some numeric arrays, sorting works just perfectly, but at some point the result may be unpredictable:

 let highScores = [11, 57, 10, 16, 32, 100]; highScores.sort(); > [10, 100, 11, 16, 32, 57] 

The point is that the sort () method produces a lexicographic comparison : this means that the numbers will be converted to a string and comparisons will be made again by matching the first character of this string in the order of the characters of the Unicode table. Therefore, we again need to define our sorting order:

 let highScores = [11, 57, 10, 16, 32, 100]; highScores.sort( function(a,b) { return a - b; } ); > [10, 11, 16, 32, 57, 100] 

Again, to sort the numbers in reverse order, swap the positions a and b in the function.

Sort JSON-like structure


And finally, if we have a JSON-like data structure , represented as an array of game records:

 let scores = [ { "name": "Daniel", "score": 21768 }, { "name": "Michael", "score": 33579 }, { "name": "Alison", "score": 38395 } ]; 

In ES6 +, you can use the arrow functions:

 scores.sort((a, b) => b.score - a.score)); 

For older browsers that do not have this support:

 scores.sort(function(a, b) { return a.score - b.score }); 

As you can see, sorting in JavaScript is not a pretty obvious thing, I hope that these examples will make life easier somehow.

To read:


1. Sort
2. Sorting algorithm
3. Bubble Sort and All-All-All
4. Natural String Sorting in JavaScript
5. Refined sorting in JavaScript
6. Javascript Sort Benchmark
7. Looking for performance? Probably you should NOT use [] .sort (V8)

Work with power functions


Exponentiation is an operation, initially defined as the result of multiple multiplication of a natural number by itself, the square root of a is a number that gives a when squared. We could use these functions constantly in everyday life during the lessons of mathematics, including when calculating areas, volumes or even during physical modeling.

In JavaScript, the power function is represented as Math.pow (), the new standard ES7 introduces a new exponentiation operator - "* *".

Exponentiation


To raise a number to the nth power, use the function Math.pow (), where the first argument is the number that will be raised to the power, the second argument is the exponent:

 Math.pow(3,2) > 9 

This form of writing means 3 squared, or 3 × 3, which leads to the result 9. You can give another example, of course:

 Math.pow(5,3); > 125 

That is, 5 cubed, or 5 × 5 × 5, is 125.

ECMAScript 7 is the next version of JavaScript, in principle, we can use the new proposed exponentiation operator - * *, this form of writing may be more intuitive:

 3 ** 2 > 9 

At the moment, the support of this operator is rather limited , so it is not recommended to use it.

Power function can be useful in a variety of situations. A simple example, calculating the number of seconds in an hour: Math.pow (60,2).

Square and cubic root


Math.sqrt () and Math.cbrt () are opposite to Math.pow (). , a — , a .

 Math.sqrt(9) > 3 

a — , a .

 Math.cbrt(125) > 5 

Math.cbrt() JavaScript , : Chrome 38+, Firefox Opera 25+ Safari 7.1+. , Internet Explorer , MDN .

Examples


, :

 Math.pow(1.25, 2); > 1.5625 Math.cbrt(56.57) > 3.8387991760286138 

, :

 Math.pow(-5,2) > 25 Math.pow(10,-2) > 0.01 

, :

 Math.sqrt(-9) > NaN 

From mathematical analysis, we know that the imaginary number is the square roots of negative numbers. And this may lead us to another technique of working with complex numbers, but that’s another story.

You can use fractional values ​​in Math.pow () to find the square and cubic roots of numbers. The square root uses a score of 0.5:

 Math.pow(5, 0.5); // = Math.sqrt(5) = 5 ** (1/2) > 2.23606797749979 

However, due to the vagaries of the floating point, you cannot exactly guess the correct result:

 Math.pow(2.23606797749979,2) > 5.000000000000001 

In such situations, you will have to resort to cutting off the signs of the number or rounding to some value.

Some, for unknown reasons, in JavaScript confuse the function Math.pow () with Math.exp () , which is an exponential function for numbers, in general. Note: in English, the “exponent” is translated as “exponent”, so this is more likely to be English-speaking, although there are alternative names for the exponent , such as index, power.

To read:


1. Math.pow (num, 2) vs (self = num) * self
2. Arithmetic square root of a negative number
3. Example of solving a quadratic equation with negative discriminant
4. Extensive mathematical library for JavaScript and Node.js
5. MathML and polyfill

Mathematical constants


Working with mathematics in JavaScript is facilitated by a number of built-in constants. These constants are properties of the Math object. It is worth paying attention that constants are written in upper case, not CamelCase notation.

Math.PI


— , . — . — , m/n, m n — . , . — 3.14159.

, JavaScript 3,141592653589793, .

, , , , : .

Math.SQRT2


2 — , 2. 2 1 ( ). . JavaScript 1.4142135623730951. (- JavaScript: Math.SQRT2 * Math.SQRT2 2).

Math.SQRT1_2


0.5 — , 2. , .

:

√(1/2) = √1 / √2 = 1 / √2 = √2 / 2

- , :

 Math.SQRT1_2 === Math.sqrt(1/2) > true Math.sqrt(2) / 2 === Math.sqrt(1/2) > true 1 / Math.sqrt(2) === Math.sqrt(1/2) > false 

Math.E


, , JavaScript . e — , , . e . JavaScript 2,718281828459045. e , .

 Math.pow(Math.E,1) > 2.718281828459045 Math.pow(Math.E,2) > 7.3890560989306495 Math.pow(Math.E,3) > 20.085536923187664 


— e, e — , 2,718281828. x — , e, x. Math.log() — x e.

 Math.log(-1); // NaN, out of range Math.log(0); // -Infinity Math.log(1); // 0 Math.log(10); // 2.302585092994046 

If you need to get the logarithm of y at the base x:

 function getBaseLog(x, y) { return Math.log(y) / Math.log(x); } getBaseLog(1/5, 5) > -1 

However, due to the peculiarities of rounding off floating-point numbers, the answer is not always accurate and is only close to the correct value:

  getBaseLog(10, 1000) > 2.9999999999999996 

Math.LN2


The Math.LN2 property represents the natural logarithm of 2 equal to 0.6931471805599453.

Math.LN10


The Math.LN10 property represents the natural logarithm of 10 equal to 2.302585092994046

Math.LOG2E


The Math.LOG2E property represents the binary logarithm of e equal to 1.4426950408889634

Math.LOG10E


The Math.LOG10E property represents the decimal logarithm of e equal to 0.4342944819032518

To read:


1. Mathematical constant
2. 10 numbers on which the world rests
3. Mathematical constant set to music

Math.abs, parseInt, parseFloat


Working with numbers in JavaScript can be much more complicated than it seems. The values ​​obtained do not always fall within the expected ranges, sometimes the result may not be at all what we expected.

Math.abs ()


The Math.abs () method returns the absolute value of a number, which reminds us of a similar mathematical function of the modulus of a .

 let newVal = -57.64; Math.abs(newVal); > 57.64 

Math.abs (0) will always return zero, but if you put a minus sign before the -Math.abs (NUM) function, we will always have a negative value.

 -Math.abs(0); > -0 

parseInt ()


We know that JavaScript understands that “15” is a string, not a number, and, for example, when parsing CSS properties with JavaScript, or by getting any value from an unprepared array, our results can be unpredictable. We could get the input string represented as “17px”, and for us this is not uncommon. The question is how to convert this string to the actual value and use it in further calculations.

Syntax: parseInt (string, radix);

parseInt , NaN. ( NaN) (string), (radix). , 10 , 8 — , 16 — . 10, 9 . , ( 16) A F.

CSS-, , , :

 let elem = document.body; let centerPoint = window.getComputedStyle(elem).transformOrigin; > "454px 2087.19px" 

:

 let centers = centerPoint.split(" "); > ["454px", "2087.19px"] 

, , :

 let centerX = parseInt(centers[0], 10); > 454 let centerY = parseInt(centers[1], 10); > 2087 

As you can see, the second argument is the number system to which the number will be converted, this parameter is optional, but it is recommended to use it, in case you do not know which line will go to the input.

parseFloat ()


From the example above, you probably noticed that parseInt drops the fractional part. In our case, parseFloat can work with floating point numbers. Again, this can be useful when parsing CSS and other tasks, especially when working with floating-point percentage.

Syntax: parseFloat (string)

 let FP = "33.33333%"; console.log(parseFloat(FP)); > 33.33333 

, parseFloat .

, parseInt() parseFloat() , , , , , .

:


1. parseInt() parseFloat()
2. parseInt() Number() JavaScript?
3. JavaScript parseInt, Convert String to Int Performance
4. Benchmark: Number vs. mul vs. parseInt vs. parseFloat

Source: https://habr.com/ru/post/312880/


All Articles