js浮点型
在编程语言中关于浮点型数字计算问题的解决方案有很多种,其实很多人都会根据个人爱好进行封装,那么首先探讨一下浮点型小数计算存在什么问题。计算机存储的数据是二进制数据,也即1、0,那么在数学中的十进制数据如何映射为二进制数?对于整数,可以除于2取余数,最后倒叙排列来得出二进制编码(负数则数取补码),例如10的二进制为1010,而-10的二进制为0110,当然看你的计算机是32位还是64位,表示数据的精度不同。对于浮点数,常有乘法表示和除法表示两种(基数为2),对于除法,当小数位大于2-n次方时(0.5,0.25,0.125,…)表示1,小于当前2-n次方为0,最后可以得出小数的二进制,例如0.5 二进制为0.1,0.52的二进制为1000010…(后面无穷),因为表示不过来,所以要截取一定的位数来近似表示,例如这里的0.52的二进制为0.10000101。我们都知道在js中进行0.1+0.2=0.30000000000000004而不是0.3,就是因为截取了小数的二进制位数造成。所以在js中进行浮点数计算时,都是在精度特定的情况下进行。
(function(win) {
//计算器
var Calc = {
add: add, substract: substract, multiply: multiply, divid: divid,
setScale: _setScale, scale: 2, isNumeric: _isNumeric,equal: equal,
getInstance: function() {
var instance = {};
//继承类
for(var name in Calc) { if(name != "getInstance") { instance[name] = Calc[name]; } }
return instance;
}
};
//加法
function add() {
var i = 0, len = arguments.length, sum = 0,
arg1 = this.isNumeric(arguments[0]) && arguments[0] || 0,
arg2 = this.isNumeric(arguments[1]) && arguments[1] || 0;
sum = _add.call(this, arg1, arg2);
if(len > 2) {
//用递归实现就是方便
sum = _add.call(this, sum, this.add.apply(this, Array.prototype.slice.call(arguments,2)));
}
return sum;
}
//两个数相加
function _add(arg1, arg2) {
var pow = Math.pow(10, this.scale), number = arg1 * pow + arg2 * pow;
return number / pow;
}
//减法
function substract() {
var i = 0, len = arguments.length, arg2 = 0,
arg1 = this.isNumeric(arguments[0]) && arguments[0] || 0;
if(len > 2) {
arg2 = this.add.apply(this, Array.prototype.slice.call(arguments,1));
} else {
arg2 = this.isNumeric(arguments[1]) && arguments[1] || 0;
}
return _substract.call(this, arg1, arg2);
}
//两个数相减
function _substract(arg1, arg2) {
var pow = Math.pow(10, this.scale), number = arg1 * pow - arg2 * pow;
return number / pow;
}
//乘法
function multiply() {
var i = 0, len = arguments.length, sum = 1,
arg1 = this.isNumeric(arguments[0]) && arguments[0] || 0,
arg2 = this.isNumeric(arguments[1]) && arguments[1] || 0;
sum = _multiply.call(this, arg1, arg2);
if(len > 2) {
sum = _multiply.call(this, sum, this.multiply.apply(this, Array.prototype.slice.call(arguments,2)));
}
return sum;
}
//两个数相乘
function _multiply(arg1, arg2) {
var pow = Math.pow(10, this.scale), number = (arg1 * pow) * (arg2 * pow);
return number / (pow * pow);
}
//除法
function divid() {
var i = 0, len = arguments.length, sum = 1,
arg1 = this.isNumeric(arguments[0]) && arguments[0] || 0,
arg2 = 1;
if(len > 2) {
arg2 = this.multiply.apply(this, Array.prototype.slice.call(arguments,1));
} else {
arg2 = this.isNumeric(arguments[1]) && arguments[1] || arg2;
}
return arg2 && _divid.call(this, arg1, arg2) || 0;
}
//两个数相除
function _divid(arg1, arg2) {
var pow = Math.pow(10, this.scale), number = (arg1 * pow) / (arg2 * pow);
return number ;
}
//设置精度
function _setScale(number, scale, notRound) {
//默认精度为2,因为电价的字段一般都是精度为2
var _scale = scale || this.scale, _pow = Math.pow(10, _scale),
_number = this.isNumeric(number) && number || 0;
if(notRound) {
//将浮点数进行提升为整数
_number = win.parseInt(_number * _pow) / _pow;
}
//toFixed会自动进行四舍五入,如果_number的精度大于_scale时
return Number(_number).toFixed(_scale);
}
//判断是否为数字
function _isNumeric(number) {
var fNaN = win.isNaN(number);
return !fNaN && !!number || fNaN && number != undefined;
}
//两个浮点数比较
function equal(arg1, arg2, scale) {
var _scale = scale || this.scale;
return Number(arg1).toFixed(_scale) === Number(arg2).toFixed(_scale);
}
//接口绑定
win = win || window;
win.Calc = Calc;
return Calc;
})(window);
注意
对于Calc.js中的Calc采用的是组件极简定定义法,同时利用工厂方法模式getInstance来创建Calc的实例对象,当然还可以采用function配合prototype来定义组件,但是这种小计算器采用极简定义法更方便些。