JavaScript
理解 js 中的双精度浮点数的原因、原理和解决方法
业务中常见的问题 原因和原理 js 所有的数字都遵循 IEE 754 标准,使用 64 位固定长度表示,这就是双精度浮点数的由来 这样做的目的是为了归一化处理数字,节省存储空间 64 位分为三部分 符号位(S):第 1 位符号位,0 代表正数,1 代表负数 指数为(E):中间 11 位指数位,代表次方数 位数位(M):最后 52 位是位数,超出的部分自动进 1 舍 0 转换公式 辅助工具: 十进制…
业务中常见的问题 原因和原理 js 所有的数字都遵循 IEE 754 标准,使用 64 位固定长度表示,这就是双精度浮点数的由来 这样做的目的是为了归一化处理数字,节省存储空间 64 位分为三部分 符号位(S):第 1 位符号位,0 代表正数,1 代表负数 指数为(E):中间 11 位指数位,代表次方数 位数位(M):最后 52 位是位数,超出的部分自动进 1 舍 0 转换公式 辅助工具: 十进制…
0.1+0.2 = 0.300000000000000041.005.toFixed(2)=1.00

0.1+0.2 = 0.30000000000000004 的原因和原理二进制和科学计数法计算器可以用来辅助计算,IEEE754 双精度 64 位计算器可以查看S、E、M的值
-----------------------------------------------
以6.5为例
1、6.5的二进制是110.1
2、110.1的科学计数法是1.101 x 10^2
------------------------------------------------
根据公式推导
符号位:S=0
指数位:E-1023=2,E=1025,1025转二进制10000000001
尾数位:1.101 去掉个位,M=101
------------------------------------------------
0.1的二进制:0.000110011001100110
0.2的二进制:0.001100110011001100
0.1在计算机中的最准确的表示:1.00000000000000005551115123126E-1
0.2在计算机中的最准确的表示:2.00000000000000011102230246252E-1
------------------------------------------------------------
js中获取到准确值
0.1.toPrecision(64) = 0.1000000000000000055511151231257827021181583404541015625000000000
0.2.toPrecision(64) = 0.2000000000000000111022302462515654042363166809082031250000000000
----------------------------------------------------------------------------------------
计算机中的计算步骤
0.1的二进制+0.2的二进制后,再转换成十进制
最终结果就是0.30000000000000004
----------------------------------------------------------------------------------------
由最终结果可以看出,js实际上保留的精度是16位,也就是说
let x = 0.1
x的保留16位,0.1.toPrecision(16),那么就是0.1000000000000000
x如果保留20位,0.1.toPrecision(20),就是0.10000000000000000555
这就是为什么看到的x=0.1不是0.10000000000000000555,而是0.1000000000000000(去掉末尾的0就是0.1)
1.005.toFixed(2)=1.00的原因和原理1.005的实际值
1.005.toPrecision(64) = 1.004999999999999893418589635984972119331359863281250000000000000
所以1.004999999999999893418589635984972119331359863281250000000000000.toFixed(2) = 1.00
// 来源:https://github.com/nefe/number-precision
'use strict';
/**
* @desc 解决浮动运算问题,避免小数点后产生多位数和计算精度损失。
* 问题示例:2.3 + 2.4 = 4.699999999999999,1.0 - 0.9 = 0.09999999999999998
*/
/**
* 把错误的数据转正
* strip(0.09999999999999998)=0.1
*/
function strip(num, precision) {
if (precision === void 0) {
precision = 12;
}
return +parseFloat(num.toPrecision(precision));
}
/**
* Return digits length of a number
* @param {*number} num Input number
*/
function digitLength(num) {
// Get digit length of e
var eSplit = num.toString().split(/[eE]/);
var len = (eSplit[0].split('.')[1] || '').length - +(eSplit[1] || 0);
return len > 0 ? len : 0;
}
/**
* 把小数转成整数,支持科学计数法。如果是小数则放大成整数
* @param {*number} num 输入数
*/
function float2Fixed(num) {
if (num.toString().indexOf('e') === -1) {
return Number(num.toString().replace('.', ''));
}
var dLen = digitLength(num);
return dLen > 0 ? strip(num * Math.pow(10, dLen)) : num;
}
/**
* 检测数字是否越界,如果越界给出提示
* @param {*number} num 输入数
*/
function checkBoundary(num) {
if (_boundaryCheckingState) {
if (num > Number.MAX_SAFE_INTEGER || num < Number.MIN_SAFE_INTEGER) {
console.warn(num + ' is beyond boundary when transfer to integer, the results may not be accurate');
}
}
}
/**
* 精确乘法
*/
function times(num1, num2) {
var others = [];
for (var _i = 2; _i < arguments.length; _i++) {
others[_i - 2] = arguments[_i];
}
if (others.length > 0) {
return times.apply(void 0, [times(num1, num2), others[0]].concat(others.slice(1)));
}
var num1Changed = float2Fixed(num1);
var num2Changed = float2Fixed(num2);
var baseNum = digitLength(num1) + digitLength(num2);
var leftValue = num1Changed * num2Changed;
checkBoundary(leftValue);
return leftValue / Math.pow(10, baseNum);
}
/**
* 精确加法
*/
function plus(num1, num2) {
var others = [];
for (var _i = 2; _i < arguments.length; _i++) {
others[_i - 2] = arguments[_i];
}
if (others.length > 0) {
return plus.apply(void 0, [plus(num1, num2), others[0]].concat(others.slice(1)));
}
var baseNum = Math.pow(10, Math.max(digitLength(num1), digitLength(num2)));
return (times(num1, baseNum) + times(num2, baseNum)) / baseNum;
}
/**
* 精确减法
*/
function minus(num1, num2) {
var others = [];
for (var _i = 2; _i < arguments.length; _i++) {
others[_i - 2] = arguments[_i];
}
if (others.length > 0) {
return minus.apply(void 0, [minus(num1, num2), others[0]].concat(others.slice(1)));
}
var baseNum = Math.pow(10, Math.max(digitLength(num1), digitLength(num2)));
return (times(num1, baseNum) - times(num2, baseNum)) / baseNum;
}
/**
* 精确除法
*/
function divide(num1, num2) {
var others = [];
for (var _i = 2; _i < arguments.length; _i++) {
others[_i - 2] = arguments[_i];
}
if (others.length > 0) {
return divide.apply(void 0, [divide(num1, num2), others[0]].concat(others.slice(1)));
}
var num1Changed = float2Fixed(num1);
var num2Changed = float2Fixed(num2);
checkBoundary(num1Changed);
checkBoundary(num2Changed);
return times(num1Changed / num2Changed, Math.pow(10, digitLength(num2) - digitLength(num1)));
}
/**
* 四舍五入
*/
function round(num, ratio) {
var base = Math.pow(10, ratio);
return divide(Math.round(times(num, base)), base);
}
var _boundaryCheckingState = true;
/**
* 是否进行边界检查,默认开启
* @param flag 标记开关,true 为开启,false 为关闭,默认为 true
*/
function enableBoundaryChecking(flag) {
if (flag === void 0) {
flag = true;
}
_boundaryCheckingState = flag;
}
// 0.1 + 0.2 = 0.30000000000000004
plus(0.1, 0.2); // 0.3
// 1.0 - 0.9 = 0.09999999999999998
minus(1.0, 0.9); // 0.1
// 3*0.3 = 0.8999999999999999
times(3, 0.3); // 0.9
// 1.21/1.1 = 1.0999999999999999
divide(1.21, 1.1); //1.1
// 0.105.toFixed(2) = 0.10
round(0.105, 2); // 0.11
暂无评论
确认操作
请再次确认编辑图片
待上传图片 (剩余 0 张)
项目详情
确认评论邮箱
邀请制评论