Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

What is the trap and solution of JavaScript floating-point number

2025-02-24 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/02 Report--

This article introduces the relevant knowledge of "what is the trap and solution of JavaScript floating point number". In the operation of actual cases, many people will encounter such a dilemma, so let the editor lead you to learn how to deal with these situations. I hope you can read it carefully and be able to achieve something!

Storage of floating point numbers

The first thing to figure out is how JavaScript stores decimals. Unlike other languages such as Java and Python, all numbers in JavaScript, including integers and decimals, have only one type-Number. Its implementation follows the IEEE 754 standard and is represented by a 64-bit fixed length, that is, the standard double double-precision floating-point number (also associated with float 32-bit single-precision). The principle of computer composition is described in detail, and it doesn't matter if you don't remember.

The advantage of this storage structure is that it can normalize integers and decimals and save storage space.

64-bit bits can be divided into three parts:

Symbol bit S: the 1st bit is a positive and negative sign bit (sign), 0 represents a positive number, and 1 represents a negative number

Exponential bit E: the 11-bit storage index (exponent) in the middle, used to represent the power.

The 52 digits of the Mantissa digit is the Mantissa (mantissa), and the excess is automatically added to a zero.

The actual number can be calculated using the following formula: "

V = (- 1) ^ {S}\ times M\ times 2 ^ {E} $

Note that the above formula follows the specification of scientific counting, which is 0m = 001 in decimal system. E is an unsigned integer because the length is 11 bits and the range of values is 0mm 2047. But the index in scientific counting can be negative, so minus an intermediate number 1023, [0meme1022] is negative and [1024d2047] is positive. For example, the index E of 4.5 is 1025 and the Mantissa M is 0 01.

The final formula becomes:

V = (- 1) ^ {S}\ times (MIMO 1)\ times 2 ^ {Emure 1023} $

So 4.5 is finally expressed as (Mumb001, Effect1025):

(the picture generates http://www.binaryconvert.com/convert_double.html) from this

The reason for the floating-point error is explained by 0.1 examples. 0.1 is converted into binary representation as 0.00011001100110011001100 (1100 loop), 1.100110011001100x2 ^-4, so the estrangement Must4 will leave out the first 1 and get 100110011. In the end, it is:

Converted to decimal, it is 0.1000000000000005551115123126, so there is a floating point error.

Why 0.1 '0.2' 0.300000000000004?

The calculation steps are as follows:

/ / 0.1 and 0.2 are converted into binary and then calculated as 0.0001100110011001100110011001100110011001100110011001100110011001100110011001100110011001100110011001100110011001100110011001100110011001100110011001100110011001100110011001100110011001100110011001100110011001100110011001100110011001100110011001100110011001100110011001100110011001100110011001100110011001100110011001100110011001100110011001100110011001100110011001100110011001100110011001100110011001100110011001100110011001100110011001100110011001

Why does xcake 0.1 get 0.1?

Congratulations on coming to the point where mountains are not mountains. Because the fixed length of mantissa is 52 bits, plus the omitted bit, the maximum number that can be expressed is 2 ^ 53 = 9007199254740992, and the corresponding scientific Mantissa is 9.007199254740992, which is the maximum precision that JS can express. Its length is 16, so you can use toPrecision (16) to do precision calculation, and the excess precision will be rounded up automatically. So there are:

0.10000000000000000555.toPrecision (16) / / returns 0.1000000000000000, which is exactly 0.1 / / after removing the last zero, but the `0.1` you see is not actually `0.1`. If you don't believe it, you can try it with higher precision: 0.1.toPrecision (21) = 0.1000000000000005551.

Large number crisis

You may have vaguely sensed what would happen if the integer was greater than 9007199254740992.

Because the M * * value is 1023, the integer that * * can represent is 2 ^ 1024-1. This is the * integer that can be represented. But you can't calculate this number this way, because it has become Infinity since 2 ^ 1024.

> Math.pow (2, 1023) 8.98846567431158e+307 > Math.pow (2, 1024) Infinity

So what happens to numbers between (2 ^ 53, 2 ^ 63)?

The number between (2 ^ 53, 2 ^ 54) will choose one of two, which can only accurately represent an even number.

The number between (2 ^ 54,2 ^ 55) will be one of four, which can only accurately represent four multiples.

... Skip more multiples of 2 in turn

The following diagram is a good representation of the correspondence between floating point numbers and real numbers (Real Number) in JavaScript. What we usually use (- 2 ^ 53, 2 ^ 53) is only a very small part in the middle, and the more we go to both sides, the more sparse and inaccurate it is.

In the early order system of Taobao, the order number was treated as a number, and then the random order number skyrocketed, which has exceeded

9007199254740992, the final solution is to change the order number into a string.

To solve the problem of large numbers, you can refer to the third-party library bignumber.js, the principle is that all the numbers as strings, re-implement the calculation logic, the disadvantage is that the performance is much worse than the original. Therefore, it is necessary to support large numbers natively. Now TC39 already has a proposal proposal bigint for Stage 3, and the problem of large numbers should be solved thoroughly.

ToPrecision vs toFixed

When dealing with data, the two functions are easily confused. What they have in common is to convert numbers into strings for display. Be careful not to use it in the middle of the calculation, only for the final result.

The difference needs to be noted:

ToPrecision is the processing precision, which is counted from left to right * non-zero numbers.

ToFixed is the rounding of the specified number of digits after the decimal point, starting from the decimal point.

Both can round up excess numbers, and some people use toFixed for rounding, but be sure to know that it has Bug.

For example, 1.005.toFixed (2) returns 1.00 instead of 1.01.

Reason: the actual corresponding number of 1.005 is 1.0049999999999999989, which is rounded off!

Solution: use the professional rounding function Math.round () to deal with it. However, Math.round (1.005 * 100) / 100 is still not good because 1.005 * 100 = 100.49999999999999. You also need to solve the precision errors of multiplication and division before using Math.round. This can be solved using the number-precision#round method described later.

Solution

Back to the most concerned question: how to solve the floating point error. First of all, in theory, it is impossible to store decimals with limited space, but we can deal with it to get the desired results.

Data display class

When you get data such as 1.4000000000000001 to display, it is recommended to use toPrecision to round it up and parseFloat to convert it into a number before displaying it, as follows:

ParseFloat (1.4000000000000001.toPrecision (12)) = = 1.4 / / True

The encapsulated method is:

Function strip (num, precision = 12) {return + parseFloat (num.toPrecision (precision));}

Why choose 12 as the default precision? This is an empirical choice, generally 12 can solve most of the 0001 and 0009 problems, and in most cases it is enough, if you need to be more accurate, you can adjust it up.

Data operation class

For operational operations, such as +-* /, toPrecision cannot be used. The right thing to do is to convert the decimal to an integer before calculating. Take addition as an example:

/ * precise addition * / function add (num1, num2) {const num1Digits = (num1.toString (). Split ('.') [1] | |''). Length; const num2Digits = (num2.toString (). Split ('.) [1] | |'). Length; const baseNum = Math.pow (10, Math.max (num1Digits, num2Digits)); return (num1 * baseNum + num2 * baseNum) / baseNum } this is the end of the introduction of "what is the JavaScript floating point trap and its solution". Thank you for reading. If you want to know more about the industry, you can follow the website, the editor will output more high-quality practical articles for you!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report