Nowadays, all kinds of frameworks and tools “run amok”, everywhere in the principle and source code, more cross-end technology needs us to explore, but if the basic skills are not good, learn what is half the effort, the effect is very bad, take time at the same time to blow confidence. This article, for my plan [light talk front end] series (five), aims to systematically and logically share the knowledge of native JavaScript with you, to help you more easily geographical clear knowledge system, better understanding and memory, I do my best, hope to live up to expectations.

Number, the protagonist of this article, can represent many things in the world of programs — status, age, price, counter, etc. However, due to the storage mechanism, Number is not very robust to adapt to all scenarios, there will be some details. In this article, Let’s discuss the application scenarios, problems and solutions of numbers along with the properties and methods of Number.

Back to basic types

In the variable article, we explained that there are originally five basic types in JavaScript, including six symbols introduced after ES6. However, there is also a “reserve” type, BigInt, which is currently in the “suggested standard stage” and can literally be called “large integers”.

A large integer is a Number that is not large enough. A large integer is a Number that is not large enough. A large integer is a Number that is not large enough. Indeed it was.

Precision “traps”

Large number of

The largest Number that Number can represent is 2 to the 53rd power minus 1. Let’s see what this number is:

9007199254740991

This value can also be obtained using number.max_safe_INTEGER, which is the largest safe integer.

Similarly, there are several values in Number:

MAX_VALUE // 1.7976931348623157e+308 number. MAX_SAFE_INTEGER // 9007199254740991 number. MIN_VALUE // 5e-324 Number.MIN_SAFE_INTEGER // -9007199254740991Copy the code

At first glance, it is too big to be read with normal counting method. Once it exceeds this value, it will be converted to scientific counting method, like 1.79e+308, and there will be some error in the accuracy.

So the question is, when do you use such a large number? This can happen, for example, when Long data is passed from the front to the back, and it needs to be handled. Previously, a common method was to convert it to a string, evaluate it bit by bit, and then combine it. Some libraries dedicated to large number processing do the same, such as big.js.

With the BigInt type, you can help handle this situation. For example, it could be defined as follows:

10n, or call BigInt()Copy the code

It is similar to Number, but different in that it cannot use methods in the Math object; Nor can it be mixed with any Number instance; both must be converted to the same type. And, of course, the natural thing to do with decimals is to drop the decimals and round them up.

With all that said, let’s see what it can do.

let testInt = 9007199254740991;
testInt * 1000000   // 9.007199254740991e+21
BigInt(testInt * 1000000)  //  9007199254740990951424n
Copy the code

As you can see, numbers are no longer “scientific” after BigInt processing. However, this type should not be used arbitrarily, only if the Number is certain to exceed 2 ^ 53, and should not be used for Number substitution.

The decimal

Since there will be precision problems, large numbers are likely to involve decimals. For example, we like to hear the “0.1 + 0.2” problem.

We enter this expression in the console and find:

0.1 + 0.2 //    0.30000000000000004
Copy the code

Instead of a 0.3, it was a long string with a 4 at the end.

Unfamiliar may be unexpected, easy for people to calculate, the computer actually made a mistake, what reason? A brief explanation:

A computer has a maximum value that it can represent and a minimum value (greater than zero) that it can represent, namely the number.min_value (5E-324) mentioned above.

The numbers in the computer are stored in binary, so it is necessary to first convert 0.1 and 0.2 into binary, for decimal to binary, the integer part divided by two mod, reverse order, decimal part multiplied by two integer, order.

0.1 to binary 0.0 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011… (loop 0011)

0.2 Binary 0.0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011… (loop 0011)

The next step is to compute two binary numbers.

Binary numbers are calculated as follows: carry “every two into one”, borrow “borrow one when two”.

The resulting binary number is 0.010011001100110011001100110011001100110011001100110100

And then you have to convert it to decimal.

The first decimal point is *2 ^ -1, the second decimal point *2 ^ -2, and so on.

The converted result is 0.30000000000000004, which is the value we saw earlier.

Since it’s a problem if the numbers are small enough, it’s also a problem with “0.3-0.2”, which gives you 0.09999999999999998.

You can’t just know the problem, you have to solve it.

  • Method one:

If it’s a problem with a small number, why don’t we just make it bigger and smaller? This is similar to the implementation of effects such as single pixels in CSS.

(0.1 + 0.2 * 1000 * 1000) / 1000 / / 0.3Copy the code
  • Method 2:

After ES6, Number added a new attribute — number. EPSILON, the minimum interval between two “representable numbers”. Provides a range of errors between numbers, let’s print it out and see:

Number. 2.220446049250313 e-16 EPSILON / /Copy the code

This is exactly equal to

2 * * - 52 / / 2.220446049250313 e-16Copy the code

As you might imagine, a range alone does not make “0.1 + 0.2 == 0.3” true, but makes the Boolean after “ignore the error” true.

Let a = 0.1 + 0.2; Let b = 0.3; Math.abs(a-b)<Number.EPSILON // trueCopy the code

It doesn’t really solve it, but it does provide a variant of the case.

Lexical “misunderstanding”

So far, two questions have been asked. In the previous article, I mentioned a common JavaScript method, toString(), which is used to convert any value of this type to a string value. Usually, you get what you want. Such as:

let a = 1; A.t oString () / / "1"Copy the code

However, if written as:

1.toString()
Copy the code

This is a bit of a puzzle. In writing, we want to convert 1 to a string, but from a numeric point of view, “1.” can also be interpreted as the first half of a decimal.

This involves lexical rules, in which a computer reads a program, or a character, not exactly as a person “intended”, but as long as what it reads follows the rules.

The smallest semantic unit in JavaScript is called a “word”, and as long as it conforms to the rules of the word, it constitutes a word.

We know that we have white space, line breaks, comments, and then we have code that has meaning.

The rule for JavaScript words is that the decimal Number can have a decimal Number, and the decimal part can be omitted before and after the decimal point, but not both

. 01 1. 1.01Copy the code

All of these are legal.

The “1.” in “1.tostring ()” will be treated as if the number after the decimal point has been omitted, and will not get the correct result, but an error.

So what’s the correct way to write it? Either of the following is acceptable:

1. ToString () // separated by a space 1.. ToString () // Add a dot in the middleCopy the code

Back on track

In order to introduce BigInt, we will introduce the most common properties and methods of Number.

toString()

I just saw toString, so let’s strike while the iron is hot.

ToString is common to many object prototypes, but each type may have its own unique effects on the common premise. For example, toString of the Number type supports passing in a radix that translates numbers to “several bases.”

let a = 10; A.tostring () // "10" does not pass the value of the default "decimal" A.tostring (2) // "1010" passes 2, will be converted to "binary" A.tostring (8) // "12" passes 8, will be converted to "octal"Copy the code

The above code is easy to see, but there are more bases overall than we usually use. For example, CSS colors, as we know them, are represented in hexadecimal notation. #FFF stands for white. So, if the base of the conversion is greater than 10, letters are used to represent numbers greater than 9.

A.tostring (16) //" A "Well, that's really a coincidenceCopy the code

parseInt()/parseFloat()

These two methods are easy to understand. They are no different on Number than on global objects, and are usually used to convert strings to numbers.

Take a look at the results:

Let a = "1.5"; parseInt(a); // 1 parseFloat(a); / / 1.5Copy the code

But there’s more to it than that. Let’s see

Let a = "1.5 b"; parseInt(a); // 1 parseFloat(a); / / 1.5Copy the code

When the string is “number + other character”, they chop off the following value and return the preceding number.

This is useful in some scenarios where numbers are required, such as a form input field price that limits the input to numbers and no other characters.

There’s a but in everything. What if the character comes first?

Let a = "c1.5 b"; parseInt(a); // NaN parseFloat(a); // NaNCopy the code

There’s really nothing you can do about it…

I’m using the input number type. I’m using the input number type. The input number interaction experience is not perfect. The binding value is also processed as a string, not as a number.

In addition to limiting the input to only numbers, sometimes also limiting the input to a few decimal places, how to do? Don’t be afraid. There is a way.

toFixed()

ToFixed () is used to reserve decimal places.

Let a = 1.5; a.toFixed() // "2"Copy the code

Ho, this look does not matter, found two problems.

If you don’t pass values, you don’t keep decimal places by default, which is obvious. The focus is on the other two:

  • The return value is a string, not a number
  • When the decimal place is 5 (or greater than 5), the result is rounded.

We can verify that again.

Let a = 1.4; a.toFixed() // "1"Copy the code

If you limit the number of decimals to two digits, enter “14.56” in the input box, and then enter an extra number “14.565”, it will be processed as “14.57”. It seems that the difference of one digit should not be allowed. So, in addition to being convenient, this is a minor flaw.

How? Remember how we handled the “0.1+0.2” problem earlier? It’s more appropriate in this case, that is, if you keep a few decimal places, you multiply by 10 times n, and then divide by 10 times n, and you get the original number.

toPrecision()

A method that works similarly to toFixed() is to preserve the precision of the value, or to put it another way, to specify the significant number of digits.

Let a = 1.4; A.torecision (2) // "1.4" A.torecision (1) // "1"Copy the code

It has the same problem of rounding off “1.6” to “2”, but it seems reasonable as a general definition of reserving a few digits, so just be careful when you use it.

At this point, the common number processing method is almost introduced, and then look at the two number judgment method.

Digital judgement

isNaN()

When you’re doing a program, there’s always an exception where you expect a number or can convert to a number, or if it doesn’t, it might be a NaN. In that case, using other values or methods to determine NaN won’t work, isNaN() comes in handy.

Number.isNaN(Number('a'))  // true
Copy the code

isInteger()

Some values are only suitable for integer representation, and you need an integer determination method. If you use the conventional method, you can check first to see if it is a number and then to see if it is a decimal. IsInteger () takes only one step.

Let a = 1.4; Number.isInteger(a) // falseCopy the code

Math

With the Number properties and methods of the Number itself out of the way, it’s time to look at another important player, the built-in Math object.

Math has many and powerful properties and methods, so let’s pick out a few commonly used ones.

Math.PI

PI, as you know, is the mathematical equivalent of PI. When we read books, we use the approximate value of PI “3.14,” and the Math method gives us ready-made attributes to call math.pi.

PI, of course, is used in circles or arcs, such as drawing a circle.

It is also often used to convert radians to degrees, so radians divided by (math.pi / 180) convert to degrees, and degrees multiplied by this number convert to radians.

Math.abs(x)

This method has been seen above, and can be used when comparing two values, if you only need to know the difference between them and don’t care who is bigger or smaller.

Math.round(x)

When talking about reserving digits in front of us, we mentioned “rounding” many times, and finally appeared.

Math.round(1.4) // 1 math.round (1.6) // 2Copy the code

“Rounding” itself is not a problem, it’s just that the default handling of some scenarios doesn’t work properly. This algorithm is a classic one, and it works in many ways, but there are other scenarios where it doesn’t work, like the next one.

Math.ceil(x)/Math.floor(x)

Sometimes, we need to do the rounding of the result, for example, the return of decimal “1.4, 1.6”, if greater than 1 are treated by 2, or less than 2 are treated by 1, we need to use the rounding algorithm.

// Up math.ceil (1.4) // 1, down math.floor (1.4) // 1, down math.floor (1.6)Copy the code

You may not remember it at first, but you need to remember that floor means “floor”, which means downward

Math.max()/Math.min()

If you’re not familiar with Math objects, and you’re faced with a set of numbers that require the highest or lowest value of them, you might want to write a comparison algorithm to get the results, but at the highest complexity, it’s not cost-effective, and there’s a good way to use it.

Let a = [1, 2, 3, 4, 5]; Math.max(... a) // 5 Math.min(... a) // 1Copy the code

PS: This method can only deal with numbers directly. Here we use the expansion operator to expand the values in the array.

Math.pow()

We’ve already seen this method before, which is to raise something to the power. And as we mentioned, there’s a new operator to do this, ‘**’.

For example, the third order of 2 can be written as “2**3”.

Math.random()

The last common method in Math is random(), which stands for “random number.”

Most of the time, regularity is neat and beautiful, but other times the lack of regularity is more natural and varied.

The random() method itself only returns a number between 0 and 1, but it can do a lot of useful things if you use your imagination.

For example, 0 to 1 can be understood as a percentage, so you can output the number between any two numbers, calculate the travel value first, then multiply the difference by the random number, and add one of them.

w = m - n;
Math.random()*w + n
Copy the code

Or, sometimes we need to repeat irregularly, the height of the ball, snow falling speed and distance, etc., we can first set a value, and then multiply it by random number, we can control the random appearance of different ranges of values from 0% to 100%.

That concludes our introduction to Math objects. There are many other methods in Math, such as trigonometry, that I won’t go into here because they are used in special situations, but you can delve into them if you need to.

conclusion

Programming digital topic, said little also, we use it well when I was a small to write, but said big also, said the input box of limited input before they take a few decimal places, you need to consider the possibility of each input is to do not bug, after all, programming is a fine job, no matter how rich the toolbox and strong, Still, the people who use it have to be clear and thoughtful to write less buggy code.

This is the fifth chapter, the journey will be half, behind will also gradually into the deep water, for you to uncover some seemingly complex concepts of mystery, we continue to come! ~

Blog link:

Digital games in JavaScript

Series of articles:

【 Talk about the front end 】 good basic skills, easy to learn with me

Small role, big use — variable

Why is everything an object?

[Talking about the front end] Those “unreasonable value” operations