Numbers

Lecture




  1. Recording methods
  2. Division by Zero, Infinity
  3. NaN
  4. isFinite(n)
  5. Conversion to number
    1. isNaN - check for numbers for strings
  6. Soft transform: parseInt and parseFloat
  7. Check for numbers for all types
  8. toString(система счисления)
  9. Rounding
    1. Rounding to a given accuracy
    2. num.toFixed(precision)
  10. Inaccurate calculations
  11. Other Mathematical Methods
    1. Trigonometry
    2. General purpose functions
  12. Total

All numbers in JavaScript, both integer and fractional, are of the Number type and are stored in the 64-bit IEEE-754 format, also known as “double precision”.

Here we look at the various subtleties associated with working with numbers in JavaScript.

Recording methods

In JavaScript, you can write numbers not only in decimal, but also in hexadecimal (starting with 0x ), as well as octal (starting with 0 ) number systems:

1 alert( 0xFF ); // 255 в шестнадцатиричной системе
2 alert( 010 ); // 8 в восьмеричной системе

Also available is an entry in the "scientific format" (they also say "floating-point entry"), which looks like <число>e<кол-во нулей> .

For example, 1e3 is 1 with 3 zeros, that is, 1000 .

1 // еще пример научной формы: 3 с 5 нулями
2 alert( 3e5 ); // 300000

If the number of zeros is negative, then the number is shifted to the right for the decimal point, so that the decimal fraction is obtained:

1 // здесь 3 сдвинуто 5 раз вправо, за десятичную точку.
2 alert( 3e-5 ); // 0.00003 <-- 5 нулей, включая начальный ноль

Division by Zero, Infinity

Imagine that you are going to create a new language ... People will call it "javascript" (or LiveScript ... whatever).

What should happen when trying to divide by zero?

As a rule, an error in the program ... In any case, in most programming languages ​​this is exactly the case.

But the creator of JavaScript decided more “mathematical” way. After all, the smaller the divider, the greater the result. When dividing by a very, very small number should be very large. In mathematical analysis, this is described in terms of limits, but if simplified, then as a result of dividing by 0 we get “infinity”, which is denoted by the symbol or, in JavaScript: "Infinity" .

1 alert(1/0); // Infinity
2 alert(12345/0); // Infinity

Infinity is a special numerical value that behaves exactly like mathematical infinity .

  • Infinity greater than any number.
  • Adding to infinity does not change it.

1 alert(Infinity > 1234567890); // true
2 alert(Infinity + 5 == Infinity); // true

Infinity can also be assigned explicitly: var x = Infinity .

It happens and minus infinity -Infinity :

1 alert( -1 / 0 ); // -Infinity

Infinity can also be obtained if you make a very large number, for which the number of digits in the binary representation does not fit in the corresponding part of the standard 64-bit format, for example:

1 alert( 1e500 ); // Infinity

NaN

If the mathematical operation cannot be performed, then the special value NaN (Not-A-Number) is returned.

For example, the division 0/0 is undefined in the mathematical sense, therefore it returns NaN :

1 alert( 0 / 0 ); // NaN

The value NaN used to denote a mathematical error and has the following properties:

  • The value of NaN is the only one of its kind that is not equal to anything, including itself .

    The following code will output nothing:

    1 if (NaN == NaN) alert( "==" ); // Ни один вызов
    2 if (NaN === NaN) alert( "===" ); // не сработает

  • The NaN value can be checked with the special function isNaN(n) , which returns true if the argument is NaN and false for any other value.
    1 var n = 0/0;
    2
    3 alert( isNaN(n) ); // true

    Another fun way to check the value on NaN is to check it for equality with itself, like this:

    1 var n = 0/0;
    2
    3 if (n !== n) alert( 'n = NaN!' );
    It works, but for clarity, it is better to use isNaN .

  • NaN value is “sticky”. Any operation with NaN returns NaN .
    1 alert( NaN + 1 ); // NaN

If the isNaN argument is not a number, then it is automatically converted to a number.

No mathematical operations in JavaScript can cause an error or crash the program.

In the worst case, the result will be NaN .

isFinite(n)

So, in JavaScript there are usual numbers and three special numerical values: NaN , Infinity and -Infinity .

The isFinite(n) function returns true only when n is a regular number and not one of these values:

1 alert( isFinite(1) ); // true
2 alert( isFinite(Infinity) ); // false
3 alert( isFinite(NaN) ); // false

If the isFinite argument is not a number, then it is automatically converted to a number.

Conversion to number

Strict conversion can be done by unary plus '+'

1 var s = "12.34" ;
2 alert( +s ); // 12.34

Strict means that if the string is not exactly a number, the result will be NaN :

1 alert( + "12test" ); // NaN

The only exception is whitespace at the beginning and end of the line, which are ignored:

1 alert( + " -12" ); // -12
2 alert( + " \n34 \n" ); // 34, перевод строки \n является пробельным символом
3 alert( + "" ); // 0, пустая строка становится нулем
4 alert( + "1 2" ); // NaN, пробел посередине числа - ошибка

The transformation in other mathematical operators and functions occurs in a similar way:

1 alert( '12.34' / "-2" ); // -6.17

Importance: 5

Create a page that prompts you to enter two numbers and displays their sum.

Should work like this: tutorial / intro / sum.html.

PS There is an "underwater stone" when working with types.

Decision

tutorial / intro / sum.html

[Open task in new window]

isNaN - check for numbers for strings

The isNaN function is mathematical, it converts an argument to a number, and then checks whether NaN is or not.

Therefore, you can use it to check:

1 var x = "-11.5" ;
2 if (isNaN(x)) {
3    alert( "Строка преобразовалась в NaN. Не число" );
4 } else {
5    alert( "Число" );
6 }

The only subtle point is that the empty string and the string of whitespace characters are converted to 0 :

1 alert(isNaN( " \n\n " )) // false, т.к. строка из пробелов преобразуется к 0 // false, т.к. строка из пробелов преобразуется к 0

And, of course, the isNaN check isNaN numbers as false, true, null , since although they are not numbers, they are converted to them:

1 + false = 0
2 + true = 1
3 + null = 0
4 + undefined = NaN;

Soft transform: parseInt and parseFloat

In the HTML / CSS world, many values ​​are not exactly numbers. For example, CSS metrics: -12px or -12px .

The '+' operator for such values ​​will return NaN :

1 alert( + "12px" ) // NaN

For easy reading of such values, there is a parseInt function:

1 alert( parseInt( '12px' ) ); // 12

parseInt and its analogue parseFloat transform a string character by symbol as long as possible.

If an error occurs, the number that was received is returned. parseInt reads an integer from a string, and parseFloat a fractional.

1 alert( parseInt( '12px' ) ) // 12, ошибка на символе 'p'
2 alert( parseFloat( '12.3.4' ) ) // 12.3, ошибка на второй точке

Of course, there are situations when parseInt/parseFloat return NaN . This happens when an error occurs on the first character:

1 alert( parseInt( 'a123' ) ); // NaN

Error parseInt('0..')

parseInt (but not parseFloat ) understands the parseFloat number system:

1 alert( parseInt( '0xFF' ) ) // 255

In the old JavaScript standard, he knew how to understand and octal:

1 alert( parseInt( '010' ) ) // в некоторых браузерах 8

If you want to be sure that the number starting from zero will be interpreted correctly - use the second optional argument parseInt - the base of the number system:

1 alert( parseInt( '010' , 10) ); // во всех браузерах 10

Check for numbers for all types

If you need a really accurate check for a number that does not count a string from spaces, logical and special values ​​- use the following function isNumeric :

function isNumeric(n) {
   return !isNaN(parseFloat(n)) && isFinite(n);
}

Let's figure out how it works. Let's start on the right.

  • The isFinite(n) function converts an argument to a number and returns true if it is not Infinity/-Infinity/NaN .

    Thus, the right-hand side will eliminate non-numbers, but leave such values ​​as true/false/null and the empty string '' , since they are correctly converted to numbers.

  • To check them you need the left side. Calling parseFloat(true/false/null/'') returns NaN for these values.

    This is how the parseFloat function parseFloat : it converts an argument to a string, i.e. true/false/null becomes "true"/"false"/"null" , and then reads a number from it, and an empty string gives NaN .

As a result, everything is eliminated, except for strings-numbers and ordinary numbers.

toString(система счисления)

As shown above, the numbers can be recorded not only in the 10-hour system, but also in the hexadecimal system. But there is the opposite task: to get a hexadecimal representation of a number. To do this, use the toString(основание системы) method toString(основание системы) , for example:

1 var n = 255;
2
3 alert( n.toString(16) ); // ff

The base can be any from 2 to 36 .

  • Base 2 is useful for debugging bit operations, which we will go a little later:
    1 var n = 4;
    2 alert( n.toString(2) ); // 100
  • The base 36 (by the number of letters in the English alphabet - 26, together with the numbers, of which 10) is used to “encode” the number as an alphanumeric string. In this number system, numbers are first used, and then letters from a to z :
    1 var n = 1234567890;
    2 alert( n.toString(36) ); // kf12oi

    With this encoding, you can make the long numeric identifier shorter, to then use it in the URL.

Rounding

One of the most frequent operations with numbers is rounding. In JavaScript, there are as many as 3 functions for this.

Math.floor
Round down
Math.ceil
Round up
Math.round
Rounds to the nearest integer.

1 alert( Math.floor(3.1) ); // 3
2 alert( Math.ceil(3.1) ); // 4
3 alert( Math.round(3.1) ); // 3

Rounding bitwise operators

Bit operators make any number a 32-bit integer, trimming the decimal part.

As a result, a bitwise operation that does not change the number, for example, a double bit NOT does round it:

1 alert( ~~12.3 ); // 12

Any bitwise operation of this kind is suitable, for example, XOR (XOR, "^" ) with a zero:

1 alert( 12.3 ^ 0 ); // 12
2 alert( 1.2 + 1.3 ^ 0); // 2, приоритет ^ меньше, чем +

This is convenient, first of all, because it is easy to read and does not force you to put additional brackets like Math.floor(...) :

var x = a * b / c ^ 0; // читается так: "a*b/c и округлить "

Rounding to a given accuracy

A common trick is to multiply and divide by 10 with the required number of zeros. For example, round 3.456 to the 2nd decimal place:

1 var n = 3.456;
2 alert( Math.round( n * 100 ) / 100 ); // 3.456 -> 345.6 -> 346 -> 3.46

This way you can round the number up and down.

num.toFixed(precision)

There is a special method num.toFixed(precision) , which rounds the number num to precision and returns the result as a string :

1 var n = 12.34;
2 alert( n.toFixed(1) ); // "12.3"

Rounding goes to the nearest value, similar to Math.round :

1 var n = 12.36;
2 alert( n.toFixed(1) ); // "12.4"

The resulting string, if necessary, is padded with zeros to the desired accuracy:

1 var n = 12.34;
2 alert( n.toFixed(5) ); // "12.34000", добавлены нули до 5 знаков после запятой

If we need exactly a number, then we can get it by applying a '+' to the result of n.toFixed(..) :

1 var n = 12.34;
2 alert( +n.toFixed(5) ); // 12.34

The toFixed method toFixed not equivalent to Math.round !

For example, let's round up to one decimal place using two methods:

1 var price = 6.35;
2
3 alert( price.toFixed(1) ); // 6.3
4 alert( Math.round(price*10)/10 ); // 6.4
As you can see, the result is different! The rounding option through Math.round is more correct, since according to generally accepted rules 5 rounded up. And toFixed can round it both up and down. Why? We will find out soon!

Inaccurate calculations

Run this example:

1 alert(0.1 + 0.2 == 0.3);

Run it? If not, do it anyway.

Ok, you launched it. The result is somewhat strange, isn't it? Perhaps a bug in the browser? Change the browser, run again.

Well, now we can be sure: 0.1 + 0.2 is not 0.3 . But then what is it?

1 alert(0.1 + 0.2); // 0.30000000000000004

As you can see, a small computational error has occurred.

The fact is that in the IEEE 754 standard, exactly 8 bytes (= 64 bits) are allocated to the number, no more and no less.

The number 0.1 (=1/10) short in decimal format, and in binary notation it is an infinite fraction (conversion of the decimal fraction to binary). Also an infinite fraction is 0.2 (=2/10) .

The binary value of the infinite fractions is stored only up to a certain sign, so inaccuracy occurs. It can even be seen:

1 alert( 0.1.toFixed(20) ); // 0.10000000000000000555

When we add 0.1 and 0.2 , then two inaccuracies are added, we get the third.

Of course, this does not mean that exact calculations for such numbers are impossible. They are possible. And even necessary.

For example, there are two ways to add 0.1 and 0.2 :

  1. Make them whole, fold, and then divide:
    1 alert( (0.1*10 + 0.2*10) / 10 ); // 0.3

    It works because the numbers 0.1*10 = 1 and 0.2*10 = 2 can be accurately represented in the binary system.

  2. Add and then round to a reasonable decimal. Rounding up to the 10th digit is usually enough to cut off the calculation error:
    1 var result = 0.1 + 0.2;
    2 alert( +result.toFixed(10) ); // 0.3

Importance: 4

In mathematics, it is assumed that 5 rounded up, for example:

1 alert( 1.5.toFixed(0) ); // 2
2 alert( 1.35.toFixed(1) ); // 1.4

But why in the example below 6.35 rounded to 6.3 ?

1 alert( 6.35.toFixed(1) ); // 6.3

Decision

In internal binary representation, 6.35 is an infinite binary fraction. It is stored with loss of accuracy .. And by the way, let's see for yourself:

1 alert( 6.35.toFixed(20) ); // 6.34999999999999964473

The interpreter sees the number as 6.34... , therefore it rounds down.

[Open task in new window]

Importance: 5

Imagine an online store. Prices are given up to a penny (cent, eurocent, etc.).

You are writing an interface for it. The main work happens on the server, but everything should be fine on the client. The addition of prices for purchased goods and their multiplication by quantity is a normal operation.

It will turn out stupid if, when ordering two products with prices of 0.10$ and 0.20$ person will receive a total cost of 0.30000000000000004$ :

1 alert( 0.1 + 0.2 + '$' );

What can I do to avoid problems with rounding errors?

Decision

There are two main approaches.

  1. You can store the prices themselves in "pennies" (cents, etc.). Then they will always be whole and the problem will disappear. But when showing and exchanging data, you will need to take this into account and do not forget to divide by 100.
  2. During operations, when it is necessary to obtain the final result - round up to the 2nd decimal place. All that is next is a rounding error:
    1 var price1 = 0.1, price2 = 0.2;
    2 alert( +(price1 + price2).toFixed(2) );
[Open task in new window]

Funny example

Hello! I am a number growing by itself!

1 alert(9999999999999999);

The reason is the same - loss of accuracy.

Of the 64 bits allocated to the number, the digits of the numbers themselves take up to 52 bits, the remaining 11 bits store the position of the decimal point and one bit is the sign. So if 52 bits are not enough for the digits, then the low bits will disappear when recording.

The interpreter will not give an error, but the result will be “not exactly the number”, which we see in the example above. As the saying goes: "as I could, I wrote it down."

For the sake of justice, we note that exactly the same thing happens in any other language where the IEEE 754 format is used, including Java, C, PHP, Ruby, Perl.

Other Mathematical Methods

JavaScript provides basic trigonometric and some other functions for working with numbers.

Trigonometry

Built-in functions for trigonometric calculations:

Math.acos(x)
Returns the arc cosine x (in radians)
Math.asin(x)
Returns the arcsine x (in radians)
Math.atan
Returns the arctangent of x (in radians)
Math.atan2(y, x)
Returns the angle to the point (y, x) . Function Description: Atan2.
Math.sin(x)
Calculates the sine of x (in radians)
Math.cos(x)
Calculates the cosine of x (in radians)
Math.tan(x)
Returns the tangent of x (in radians)

General purpose functions

Various useful features:

Math.sqrt(x)
Returns the square root of x .
Math.log(x)
Returns the natural (base e ) logarithm of x .
Math.pow(x, exp)
Raises the number to the power, returns x exp , for example, Math.pow(2,3) = 8 . It also works with fractional and negative powers, for example: Math.pow(4, -1/2) = 0.5 .
Math.abs(x)
Returns the absolute value of a number.
Math.exp(x)
Returns e x , where e is the base of the natural logarithms.
Math.max(a, b, c...)
Returns the largest of the list of arguments.
Math.min(a, b, c...)
Returns the smallest of the argument list.
Math.random()
Returns a pseudo-random number in the range [0,1 0,1) - that is, between 0 (inclusive) and 1 (excluding). The random number generator is initialized by the current time.

Total

  • Numbers can be written in hexadecimal, octal, as well as "scientific" way.
  • In JavaScript, there is a numeric value infinity Infinity .
  • Calculation error gives NaN .
  • Arithmetic and mathematical functions convert a string exactly to a number, ignoring leading and trailing spaces.
  • The parseInt/parseFloat make numbers from strings that start with a number.
  • There are four rounding methods: Math.floor , Math.round , Math.ceil and the bit operator. To round to the desired character, use +n.toFixed(p) or a trick with multiplication and division by 10 p .
  • Fractional numbers give a calculation error. If necessary, it can be cut off by rounding to the desired character.
  • Random numbers from 0 to 1 generated using Math.random() , the rest are converted from them.

There are other math functions. You can find them in the directory in the sections Number and Math.

Importance: 4

This cycle is endless. Why?

1 var i = 0;
2 while (i != 10) {
3    i += 0.2;
4 }

Decision

Because i will never be equal to 10 .

Run to see real i values:

1 var i = 0;
2 while (i < 11) {
3    i += 0.2;
4    if (i>9.8 && i<10.2) alert(i);
5 }

None of them is exactly 10 .

[Open task in new window]

Importance: 3

Write the getDecimal(num) function that returns the decimal portion of a positive number:

alert( getDecimal(12.5) ); // 0.5
alert( getDecimal(6.25) ); // 0.25

Please do not convert the number to a string in the process.

Answer two questions:

  • Does your function work exactly for most numbers?
  • If not, but there is information that the fractional part can be no more than 6 decimal places - is it possible to correct the function so that it works correctly?
Decision
Function

The function may be:

1 function getDecimal(num) {
2    return num - Math.floor(num);
3 }
4
5 alert( getDecimal(12.5) ); // 0.5
6 alert( getDecimal(6.25) ); // 0.25

... Or, much simpler, like this:

function getDecimal(num) {
   return num % 1;
}

Numbers

Usually, the function does not work correctly due to inaccurate framing algorithms.

For example:

1 alert( 1.2 % 1 ); // 0.19999999999999996
2 alert( 1.3 % 1 ); // 0.30000000000000004
3 alert( 1.4 % 1 ); // 0.3999999999999999

Correction

You can add toFixed rounding to drop extra characters:

1 function getDecimal(num) {
2    return +(num % 1).toFixed(6);
3 }
4
5 alert( getDecimal(1.2) ); // 0.2
6 alert( getDecimal(1.3) ); // 0.3
7 alert( getDecimal(1.4) ); // 0.4

[Open task in new window]

Importance: 4

The sequence of Fibonacci numbers has the formula F n = F n-1 + F n-2 . That is, the following number is obtained as the sum of the two previous ones.

The first two numbers are 1 , then 2(1+1) , then 3(1+2) , 5(2+3) and so on: 1, 1, 2, 3, 5, 8, 13, 21... .

The code to calculate them (from the Fibonacci number problem):

1 function fib(n){
2    var a=1, b=0, x;
3    for (i=0; i<n; i++) {
4      x = a+b;
5      a = b
6      b = x;
7    }
8    return b;
9 }

There is a Binet formula, according to which F n is equal to the nearest integer for ϕ n /√5 , where ϕ=(1+√5)/2 is the golden ratio.

Write a function fib(n) that will compute F n using this formula. Check it for the F 77 (должно получиться fib(77) = 5527939700884757 ). value F 77 (должно получиться fib(77) = 5527939700884757 ).

Is the result correct? If not, why not?

Decision

Calculation according to the corollary of Binet’s formula:

1 function fib(n) {
2    var phi = (1 + Math.sqrt(5)) / 2;
3    return Math.round( Math.pow(phi, n) / Math.sqrt(5) );
4 }
5
6 alert( fib(2) ); // 1, верно
7 alert( fib(8) ); // 21, верно
8 alert( fib(77)); // 5527939700884755 != 5527939700884757, неверно!

Note that the calculation uses the rounding of Math.round , since you need exactly the nearest integer .

The result of calculating the F 77 wrong!

It differs from that calculated in another way. The reason is in rounding errors, because √5 is an infinite fraction.

Rounding errors in the calculations are multiplied and, as a result, give a discrepancy.

[Open task in new window]

Importance: 2

Write code to generate a random value in the range from 0 to max , not including max .

Decision

Generate a value in the range 0..1 and multiply by max :

1 var max = 10;
2
3 alert( Math.random()*max );

[Open task in new window]

Importance: 2

Write code to generate a random number from min to max , not including max .

Decision

Generate a value from the interval 0..max-min , and then shift to min :

1 var min=5, max = 10;
2
3 alert( min + Math.random()*(max-min) );

[Open task in new window]

Importance: 2

Write a code to generate a random integer between min and max , including min,max as possible values.

Any number from the interval min..max should have the same probability.

Decision
The obvious wrong decision (round)

The simplest, but wrong way is to generate a value in the interval min..maxand round it Math.round, like this:

var rand = min + Math.random()*(max-min)
rand = Math.round(rand);

It is working. But at the same time the probability of getting the extreme values minand maxis two times less than any other.

For example, let's find values ​​between 1 and 3 in this way:

// случайное число от 1 до 3, не включая 3
var rand = 1 + Math.random()*(3-1)

The call will Math.round()round the values ​​as follows:

значения из диапазона 1 ... 1.499+ станут 1
значения из диапазона 1.5 ... 2.499+ станут 2
значения из диапазона 2.5 ... 2.999+ станут 3

From here it is already clear that the range 1(as well as 3) falls in two times smaller than that in 2. So it 1will be issued twice less than 2.

Right decision with round

The right way: Math.round(случайное от min-0.5 до max+0.5)

1 var min = 1, max = 3;
2
3 var rand = min - 0.5 + Math.random()*(max-min+1)
4 rand = Math.round(rand);
5
6 alert(rand);

In this case, the range will be the same ( max-min+1), but the rounding mechanics are taken into account round.

Floor solution

Альтернативный путь - применить округление Math.floor() к случайному числу от min до max+1 .

Например, для генерации целого числа от 1 до 3 , создадим вспомогательное случайное значение от 1 до 4 (не включая 4 ).

Тогда Math.floor() округлит их так:

1 ... 1.999+ станет 1
2 ... 2.999+ станет 2
3 ... 3.999+ станет 3

Все диапазоны одинаковы.
Итак, код:

1 var min=5, max=10;
2 var rand = min + Math.random()*(max+1-min);
3 rand = rand^0; // округление битовым оператором
4 alert(rand);

[Open task in new window]
created: 2014-10-07
updated: 2021-03-13
132572



Rating 9 of 10. count vote: 2
Are you satisfied?:



Comments


To leave a comment
If you have any suggestion, idea, thanks or comment, feel free to write. We really value feedback and are glad to hear your opinion.
To reply

Scripting client side JavaScript, jqvery, BackBone

Terms: Scripting client side JavaScript, jqvery, BackBone