number 小数计算会出现精度不准确问题,js中number是64 位双精度浮点数。
其实,不仅仅只有javascript,还有java、python等都会有类似问题,因为计算机中存的都是二进制(浮点数IEEE754是被普遍使用的标准),小数转二进制时是乘2取整,再用余下的小数部分乘2再取整,如此反复。有的小数转二进制可能会无限循环比如0.1、0.2等,计算机中不可能提供无限个bit位去存储它们,会舍弃一部分二进制位,因而造成了精度损失。
而且在加减乘除运算完毕后,由于尾数的位数有限,需要进行舍入(舍弃多余的位数) 常用方法有0舍1入、恒置1
0.1+0.2=0.30000000000000004
0.1 + 0.2 === 0.3 //为false
以下是一些常见的解决方法:
1.先转为整数并计算然后再转回小数:
function add(num1, num2){
const getDecimalLen = (numStr) => numStr.includes('.') ? (numStr.split('.')[1]).length : 0,//获取小数长度
num1Len = getDecimalLen(num1.toString()),
num2Len = getDecimalLen(num2.toString()),
factor = Math.pow(10, Math.max(num1Len, num2Len));//10的maxLen次方
return (num1 * factor + num2 * factor) / factor;
}
add(0.1, 0.2); // 0.3
2.使用专门的库或工具:
在处理需要高精度计算的场景中,可以使用一些专门的库或工具。例如,JavaScript 中的 Decimal.js、Big.js 或 BigNumber.js 等库提供了高精度的数学计算功能,可以避免精度丢失的问题。
var Decimal = require('decimal.js');
new Decimal(0.1).add(new Decimal(0.2)).toNumber(); //0.3
python中可以使用SciPy、Numpy等。例如,SciPy中的derivative函数可以用于计算数值导数,而NumPy中的梯度函数则可以用于计算向量或矩阵的梯度
java中可使用jdk8的BigDecimal(不支持三角函数) 和 big-math拓展的BigDecimalMath(支持三角函数)
比如 求2的平方根(保留6位精度):BigDecimalMath.root(new BigDecimal(2), new BigDecimal(2) , new MathContext(6));
对于divide除数计算需要指定保留精度,否则除出来无限循环会报此错误:Non-terminating decimal expansion; no exact representable decimal result
BigDecimal.divide(new BigDecimal(8), new MathContext(10));// 第二个参数传入MathContext指定精度
BigDecimal.divide(new BigDecimal(8), 8, BigDecimal.ROUND_HALF_DOWN);//第二个参数保留几位小数,第三个参数为四舍五入规则
3.toFixed限制小数位数(返回字符串):
这种虽然简便,但是由于浮点数IEEE754规则的限制,不能够准确的达到四舍五入的效果。
1.35.toFixed(1); // '1.4' 正确
1.335.toFixed(2) // '1.33' 错误
解决toFixed问题:
转为整数,取舍后再转回小数, 但是Math.round对于负数的四舍五入处理时还存在一定问题。
Number.prototype.toFixed = function(size) {
const padDecimal = (num, decimalPlaces) => {
let numStr = num.toString();
if (numStr.includes('.')) { //有小数,补0
let decimalPart = numStr.split('.')[1];
if (decimalPart.length < decimalPlaces) {
numStr += '0'.repeat(decimalPlaces - decimalPart.length);
}
} else { // 如果没有小数部分,添加小数点和0
numStr += '.';
numStr += '0'.repeat(decimalPlaces);
}
return numStr;
}
return padDecimal((Math.round(this * Math.pow(10, size)) / Math.pow(10, size)), size);//还需要补充缺失的0
}
1.335.toFixed(2); //'1.34' 正确
(-1.335).toFixed(2);//'-1.33' 错误
-1.335.toFixed(2); //'-1.34' 正确 是因为'.'运算符优先级高于'-'
常见的舍入策略包括四舍五入Math.round、向上取整Math.ceil、向下取整Math.floor。
但是注意:Math.round对于负数的处理会存在一定问题。 floor和ceil不会出现问题。
Math.round(-5.4); // -5 正确
Math.round(-5.5); // -5 错误
Math.round(-5.6); // -6 正确
Math.round方法准确说是“四舍六入”,对0.5要进行判断对待。
Math.round的原理是对传入的参数+0.5之后,再floor向下取整得到的数就是返回的结果。
public static long round(double a) {
if (a != 0x1.fffffffffffffp-2) // greatest double value less than 0.5
return (long)floor(a + 0.5d);
else
return 0;
}
优化之后:
//先记录数值正负,保证round操作的是正数,在最后返回时,再进行修改正负
Number.prototype.toFixed = function(size) {
const padDecimal = (num, decimalPlaces) => {
let numStr = num.toString();
if (numStr.includes('.')) { //有小数,补0
let decimalPart = numStr.split('.')[1];
if (decimalPart.length < decimalPlaces) {
numStr += '0'.repeat(decimalPlaces - decimalPart.length);
}
} else { // 如果没有小数部分,添加小数点和0
numStr += '.';
numStr += '0'.repeat(decimalPlaces);
}
return numStr;
}
let abs = 1;
if (this < 0) abs = -1;
const _this = Math.abs(this);
let factor = Math.pow(10, size);
let result = Math.round(_this * factor) / factor;
//补充缺失的0
return padDecimal(result * abs, size);
}
1.335.toFixed(2); //'1.34' 正确
(-1.335).toFixed(2);//'-1.34' 正确
高等数学中一些基本概念
在数学和计算机中三角函数的所有计算都采用弧度制(pi/2),而不是角度制。
其实,如果只是为了度量一个角度,用“角度”是满方便的.但是,在数学和工程技术中,大量用到三角函数和相关公式。用角度制的话相关公式比较复杂,不便计算。
而使用弧度制的话, 公式是相对最简单的。如下图所示:
radian 弧度 degress角度
三角函数(sin/cos/tan) 反三角函数等在计算机中一般传入的是弧度,而非角度。需转换(弧度=Math.PI/180 * 角度)。
exp,高等数学里以自然常数e为底的指数函数。exp(2)就是e的平方。
log:表示对数,与指数相反
root:表示开根计算。 sqrt 平方根
pow:表示幂,计算n次幂
add、subtract、multiple、divide 加减乘除
remainder %取余 negate 取反(-this)
笛卡尔积:两个集合X和Y的笛卡尔积(Cartesian product),又称直积,表示为X × Y,第一个对象是X集合的成员而第二个对象是Y集合的成员所有可能有序对。
通俗的解释就是:用来表示两个团体中融合时候,两个团体中的每一个个体之间会产生什么样的可能性
Mysql中多表查询就是利用笛卡尔积生成一个虚拟表,然后再去除其中无关的记录。
微分、求导、积分相关概念: