JavaScript subtraction crisis - why such a result?

In their daily work calculations, we treading on thin ice, but JavaScript always give us this kind of surprise ~

  1. 0.1 + 0.2 = ?
  2. 1 - 0.9 = ?

If the junior partner given the results of the heart:

  1. 0.1 + 0.2 = 0.3
  2. 1 - 0.9 = 0.1

So small partners will be severely fan face facts:

console.log(0.1 + 0.2); // 0.30000000000000004
console.log(1 - 0.9); // 0.09999999999999998

Why does this happen? Let's check it out!

III to reproduce

Back to Contents

Below, we will discuss by the IEEE 754 standard, as well as the calculation of addition and subtraction JavaScript to reproduce the problem.

3.1 root cause: IEEE 754 standard

Back to Contents

The number in JavaScript using the IEEE 754 standard 64-bit double precision floating point. This specification defines a floating point format for the 64-bit floating-point representations in memory, one is the highest symbol, then the index is 11, the remaining valid number 52, specifically:

  • Bit 0: the sign bit. Represented by s 0 indicates a positive number, expressed as a negative number;
  • 1 - 11: store the exponent part. Represented by E;
  • 12 - 63: a storage fractional part (i.e., significant figures). He is represented by f.

 

JavaScript subtraction crisis - why such a result?

 

 

A negative sign bit determines the number, the size of the index values ​​determined in part, determine the accuracy of the fractional part of the value.

IEEE 754 provides the first significant digit default is always 1, not saved in 64-bit floating-point number.

In other words, always effective digital form 1.XX ...... XX, where XX ...... XX portion stored in 64-bit floating-point numbers, can take up to 52.

Thus, JavaScript provides maximum significant digits of 53 bits (52 + 64-bit floating-point number after effectively the first bit 1).

3.2 reproduction: the calculation process

Back to Contents

When calculated by JavaScript 0.1 + 0.2, what will happen?

1, 0.1 and 0.2 into binary representation:

0.1 -> 0.0001100110011001 ... (unlimited) 
0.2 -> 0.0011001100110011 ... (unlimited

Floating point binary expressions are endless

2, since the 64-bit double-precision floating-point fractional part of the IEEE 754 standard supports a maximum of 53 bits, so that after the addition of the two binary obtain:

0.0100110011001100110011001100110011001100110011001100

Because the floating-point decimal places restrictions, this binary number is truncated, converting this binary number into a decimal, it would .30000000000000004, thereby generating error arithmetic computation is performed.

3.3 Extensions: Digital Security

Back to Contents

After reading the above calculation decimal inaccurate, jsliang feel the need to talk to another integer, integer because there are also some problems:

console.log(19571992547450991);
// 19571992547450990

console.log(19571992547450991 === 19571992547450994);
// true

It is not very surprising!

Because JavaScript in Number Type unified by the floating-point processing, integer can not escape the question:

// 最大值
const MaxNumber = Math.pow(2, 53) - 1;
console.log(MaxNumber); // 9007199254740991
console.log(Number.MAX_SAFE_INTEGER); // 9007199254740991

// 最小值
const MinNumber = -(Math.pow(2, 53) - 1);
console.log(MinNumber); // -9007199254740991
console.log(Number.MIN_SAFE_INTEGER); // -9007199254740991

That is an integer ranging from security: [-9,007,199,254,740,991, 9007199254740991].

More than this range, there are accuracy problems are discarded.

Of course, this problem does not only exist in JavaScript, almost all using the IEEE-745 standard programming language, will have this problem, but in many other languages ​​have a good package of ways to avoid the problem of accuracy.

  • PHP Float Float - Manual
  • Java your decimal point go? - Brian Goetz

And because JavaScript is a loosely typed language, there is no strict data types to float from design thinking, the problem of accuracy error is particularly prominent.

So far, we can see that JavaScript when processing the digital type of operation, may have some problems.

In fact, the work really be a problem!

One day I deal with the calculation of a worksheet, and then the next day was told that the line in question, after being questioned Product little sister:

  • Why decimal students can make calculations, you computer Suan Bule it?

Silence three seconds, and generated above to find exploration, finally found the following solution.

Four problem-solving

Back to Contents

The following attempt to solve the problem of floating-point computations in various ways.

4.1 toFixed()

Back to Contents

toFixed () method uses a fixed-point notation numerical format.

  • 《toFixed - MDN》

Syntax: numObj.toFixed (digits)

Parameters: digits. The number of digits after the decimal point; between 0 and 20 (inclusive), the environment may be implemented to support a wider range. If omitted, the default is 0.

12345.6789 NUM = const; 

num.toFixed (); // '12346': rounding, decimal part is not included.
num.toFixed (1); // '12345.7 ': rounding, decimal places one digit.
num.toFixed (6); // '12345.678900 ': Reserved 6 decimal digits, is filled with the insufficient length 0.
(1.23e + 20) .toFixed (2 ); // 123000000000000000000.00 scientific notation into normal digital type

toFixed the results () of type String, remember to convert Number type.

toFixed () method uses a fixed point number format notation, the result will be rounded.

By toFixed () we can solve some of the problems:

Original Modified multiplier:

console.log(1.0 - 0.9);
// 0.09999999999999998

console.log(0.3 / 0.1);
// 2.9999999999999996

console.log(9.7 * 100);
// 969.9999999999999

console.log(2.22 + 0.1);
// 2.3200000000000003

Use toFixed ():

@ Formula: parseFloat ((mathematical expression) .toFixed (digits)); 
// toFixed () parameter the accuracy must be between 0 and 20 is

parseFloat ((1.0 - 0.9) .toFixed (10));
// 0.1

parseFloat ((0.3 / 0.1) .toFixed (10));
//. 3

parseFloat ((9.7 * 100) .toFixed (10));
// 970

parseFloat ((2.22 + 0.1) .toFixed (10));
// 2.32

So, talking about this, the question arises:

  • parseFloat(1.005.toFixed(2))

What would get it, your reaction is not 1.01?

Yet it is not, the result is: 1.

Say so, enm ... throw! o (╥﹏╥) o

toFixed () it has been proven nor is the safest solution.

Handwritten Math Easy 4.2

Back to Contents

Since JavaScript own methods can not help themselves, then we can only change in thinking:

  • The decimal part converted into a string of JavaScript calculated
/ ** 
* detected data whether the @name overrun
* @param {Number The Number}
* /
const checkSafeNumber = (Number) => {
IF (Number> Number Number.MAX_SAFE_INTEGER || <Number.MIN_SAFE_INTEGER) {
the console.log ( ` digital $ {number} overrun, please pay attention to risk `);!
}
};

/ **
* @name correction data
* @param {number} number needs to be corrected digital
* @param {number} precision correct number of digits
* /
the Revise = const (Number, Precision = 12 is) => {
return parseFloat + (Number.toPrecision (Precision));
}

/ **
* Get the @name length of the decimal point
* @param {Number} converted digital need
* /
const = digitLength (Number) => {
return (Number.toString () Split () [. 1] || ''. '.') .length;
};

/ **
* @Name the decimal digits removed
* @param {Number} converted digital need
* /
const floatToInt = (Number) => {
return Number The (Number.toString () Replace (, ''). '.');
} ;

/ **
* precision multiplication, the @name
* @param {Number} arg1 multiplier. 1
* @param {} arg2 Number The multiplier 2
* /
const = multiplication (arg1, arg2) => {
const = BaseNum digitLength (arg1) digitLength + (arg2);
const = Result floatToInt (arg1) * floatToInt (arg2);
checkSafeNumber (Result);
return Result / Math.pow (10, BaseNum);
for an integer division two integers // safe range is not problem
// If so, prove to me
};

console.log ( '------ \ the n-multiplication:');
console.log (9.7 * 100); // 969.9999999999999
the console.log (multiplication (9.7, 100)); // 970

the console.log (0.01 * 0.07); // .0007000000000000001
the console.log (multiplication (0.01, 0.07)); // 0.0007

the console.log (1207.41 * 100) ; // 120,741.00000000001
the console.log (multiplication (1207.41, 100)); // 0.0007

/ **
* the @name adder accuracy computing
errors * @description JavaScript addition result, two floating-point number 0.1 0.3 + 0.2 ==,! this error can be removed using the method.
* @Param {Number} arg1 addend. 1
* @param {Number The addend arg2} 2
* + arg1 arg2 @return
* /
const = the Add (arg1, arg2) => {
const = BaseNum Math.pow (10, the Math. max (digitLength (arg1), digitLength (arg2)));
return (multiplication (arg1, BaseNum) + multiplication (arg2, BaseNum)) / BaseNum;
}

the console.log ( '------ \ n-addition:' );
the console.log (1.001 + 0.003); // 1.0039999999999998
the console.log (the Add (1.001, 0.003)); // 1.004

the console.log (3.001 + 0.07); // 3.0709999999999997
the console.log (the Add (3.001, 0.07)) ; // 3.071

/ **
* the @name accuracy computing subtraction
* @param {Number} arg1 subtrahend. 1
* @param {} arg2 Number The minuend 2
* /
const = subtraction (arg1, arg2) => {
const = BaseNum the Math .pow (10, Math.max (digitLength (arg1), digitLength (arg2)));
return (multiplication (arg1, BaseNum) - multiplication (arg2, BaseNum)) / BaseNum;
};

the console.log ( '--- --- \ n-subtraction: ');
the console.log (0.3 - 0.1); // 0.19999999999999998
the console.log (subtraction (0.3, 0.1)); // 0.2

/ **
* the @name accuracy computing division
* @param { Number} arg1 divisor
* @param {Number} arg2 除数 2
*/
const division = (arg1, arg2) => {
const baseNum = Math.pow(10, Math.max(digitLength(arg1), digitLength(arg2)));
return multiplication(arg1, baseNum) / multiplication(arg2, baseNum);
};

console.log('------\n除法:');
console.log(0.3 / 0.1); // 2.9999999999999996
console.log(division(0.3, 0.1)); // 3

console.log(1.21 / 1.1); // 1.0999999999999999
console.log(division(1.21, 1.1)); // 1.1

console.log(1.02 / 1.1); // 0.9272727272727272
console.log(division(1.02, 1.1)); // 数字 9272727272727272 超限,请注意风险!0.9272727272727272

console.log(1207.41 / 100); // 12.074100000000001
console.log(division(1207.41, 100)); // 12.0741

/**
* @Name rounding specified number of digits
* @param {Number} number offs required number
* @param {Number} ratio to the nearest decimal number
* /
const = round (Number, ratio) => {
const = BaseNum Math.pow (10, ratio);
return Division (Math.round (multiplication (Number the, BaseNum)), BaseNum);
// after Math.round () were rounding one decimal place if there is a problem, if there is, please prove it
// https : //developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Math/round
}

the console.log ( '------ \ n-rounded:');
the console.log (0.105 .toFixed (2)); // '0.10'
the console.log (round (0.105, 2)); 0.11 //

the console.log (1.335.toFixed (2)); // '1.33'
the console.log (round ( 1.335, 2)); 1.34 //

the console.log (-Round (2.5, 0)); // -3
the console.log (-Round (20.51, 0)); // -21

In this code, we first, calculated by the calculating digital converted into an integer by multiplying the rock hammers, resulting in a secure data.

JavaScript integer arithmetic will not be problematic?

Calculate the multiplication, multiplication has been assumed that no problem, then the introduction of addition, subtraction, division and multiplication by the three arithmetic.

Finally, rounding rules made by multiplication and division.

Digital JavaScript Math.round () generated by it will not be a problem,

Thus, we get the addition, subtraction and rounding two numbers (reserved specified length), then there will not be a problem?

If so, please list them.

If not, then you can not be in accordance with the above addition, subtraction of two numbers, three numbers plus or minus realize even more the number of multiplication and division?

Five ready-made framework

Back to Contents

Such an important calculation, if they write, then you will always feel insecure, feeling full of crisis.

So many times, we can use the bigwigs written JavaScript Computing Base, as they Gangster has helped us a great deal of testing, and greatly reduce the problems of our handwriting problems, so we can call someone else to write the class library.

The following recommended several good libraries:

  • Math.js。

Math.js is one for JavaScript and Node.js extension math library.

It has a flexible support symbolic computation expression parser, a large number of built-in functions and constants, and provides an integrated solution to handle different types of data, such as numbers, large numbers, complex numbers, fractions, and the unit matrix.

Powerful and easy to use.

  • decimal.js

JavaScript decimal precision of any type.

  • big.js

A small, fast, easy to use library for arbitrary-precision decimal arithmetic.

  • bignumber.js

One for arbitrary precision arithmetic JavaScript library.

Finally, the last, it is worth mentioning: If the calculation of the numbers is very strict, perhaps you can use parameters threw back end, so that the rear end of the calculation, and then returns the results to you.

For example relates to the bit credits, commodity prices calculated mall ~

Guess you like

Origin www.cnblogs.com/guchengnan/p/11959748.html