JS:学好加、减、乘、除运算,走遍天下都不怕

进入我的主页,查看更多JS的分享!
这是另一种实现方式,更简洁、完整(原先有一篇实现了,可以对比这一篇的代码看看)

一般涉及到价格的计算,比如商城的下单结算,多数是交由后端来计算的。但是也会有前台的参与,遇到也不要慌,下面的代码直接解决了js的计算问题。
有问题请在评论区告知,没有就点个赞呗!

一、加法、减法

1. 出错的例子

console.log(0.1 + 0.2);
//输出:0.30000000000000004,正确结果为0.3
console.log(0.2 + 0.4)
//输出:0.6000000000000001,正确结果为0.6
 
console.log(1.0 - 0.9);
//输出:0.09999999999999998,正确结果为0.1
console.log(0.9 - 0.7);
//输出:0.20000000000000007,正确结果为0.2

2. 解析

出错是因为浮点数运算的时候,先转化为二进制,用二进制来计算,得到的结果再转回十进制。
简单理解为js里的非整数运算会出错,下面是解决的代码(推荐非整数的运算都经过处理而不是直接计算)。


//加法运算
var calcPlus = (num1, num2) => {
  let l1, l2, m;
  try {
    l1 = num1.toString().split(".")[1].length;
  } catch (e) {
    l1 = 0;
  }
  try {
    l2 = num2.toString().split(".")[1].length;
  } catch (e) {
    l2 = 0;
  }
  m = Math.pow(10, Math.max(l1, l2));
  return (num1 * m + num2 * m) / m;
};
 
console.log(calcPlus(0.1, 0.2));
//输出:0.3
console.log(calcPlus(0.2, 0.4));
//输出:0.6

//减法运算
var calcSubtract = (num1, num2) => {
  let l1, l2, m;
  try {
    l1 = num1.toString().split(".")[1].length;
  } catch (e) {
    l1 = 0;
  }
  try {
    l2 = num2.toString().split(".")[1].length;
  } catch (e) {
    l2 = 0;
  }
  m = Math.pow(10, Math.max(l1, l2));
  return (num1 * m - num2 * m) / m;
};
 
console.log(calcSubtract(1.0, 0.9));
//输出:0.1
console.log(calcSubtract(0.9, 0.7));
//输出:0.2

对比这两段代码会发现,实现原理是一样的:

  • 得到中间值,是两个数字的最大的小数位,并由10的次方转换为实际的值;
  • 最终,数字1、数字2经中间值变为整数,计算后再经中间值得到正确的结果;
  • 比如,0.1 + 0.2 => (0.1x10 + 0.2x10) / 10 = 3 / 10 = 0.3;
  • 比如,0.9 - 0.7 => (0.9x10 - 0.7x10) / 10 = 2 / 10 = 0.2

二、乘法

1. 出错的例子

console.log(19.9 * 100);
//输出:1989.9999999999998,正确结果为1990
console.log(79.9 * 100);
//输出:7990.000000000001,正确结果为7990

2. 解析

出错的原因,一样是转为二进制后再进行计算。先贴上代码:

//乘法运算
var calcMultiply = (num1, num2) => {
  let m = 0;
  try {
    m += num1.toString().split(".")[1].length;
  } catch (e) {}
  try {
    m += num2.toString().split(".")[1].length;
  } catch (e) {}
  return (Number(num1.toString().replace(".", "")) * Number(num2.toString().replace(".", ""))) / Math.pow(10, m);
};
 
console.log(calcMultiply(19.9, 100));
//输出:1990
console.log(calcMultiply(79.9, 100));
//输出:7990

实现原理:

  • 得到中间值,是两个数字的小数位之和,并由10的次方转换为实际的值;
  • 将数字1、数字2去除小数点,即变为整数,相乘后除以中间值,得到正确的结果;
  • 比如:19.9 * 100 => (199 * 100) / 10 = 19900 / 10 = 1990

三、除法

1. 出错的例子

console.log(6.6 / 0.2);
//输出:32.99999999999999,正确结果为33
console.log(99.9 / 3);
//输出:33.300000000000004,正确结果为33.3

2. 解析

出错的原因,一样是转为二进制后再进行计算。先贴上代码:

//除法运算
var calcDivide = (num1, num2) => {
  let l1 = 0, l2 = 0;
  try {
    l1 = num1.toString().split(".")[1].length;
  } catch (e) {}
  try {
    l2 = num2.toString().split(".")[1].length;
  } catch (e) {}
  let c = Number(num1.toString().replace(".", "")) / Number(num2.toString().replace(".", ""));
  return calcMultiply(c , Math.pow(10, l2-l1));
};
 
console.log(calcDivide (6.6, 0.2));
//输出:33
console.log(calcDivide (99.9, 0.3));
//输出:333

实现原理:

  • 得到中间值,是两个数字的小数位之差,并由10的次方转换为实际的值;注意除法的除数位与被除数位是固定的,因此上面代码是l2-l1;
  • 将数字1、数字2去除小数点,即变为整数,相除后乘以中间值(调用上面的乘法的计算,因为会存在小数),得到正确的结果;
  • 比如:99.9/3 => (999 / 3) * 0.1 = 333 * 0.1 = 33.3

四、拓展封装(完整代码)

上面的代码还有重复的地方,那就再进一步封装:

/**
 * calc.plus 加法运算
 * calc.subtract 减法运算
 * calc.multiply 乘法运算
 * calc.divide 除法运算
 */
 
var calc = (function () {
  let operation = (num1, num2, op) => {
    let l1, l2, max;
    try {
      l1 = num1.toString().split(".")[1].length;
    } catch (e) {
      l1 = 0;
    }
    try {
      l2 = num2.toString().split(".")[1].length;
    } catch (e) {
      l2 = 0;
    }
    switch (op) {
      case "plus":
        max = Math.pow(10, Math.max(l1, l2));
        return (num1 * max + num2 * max) / max;
      case "subtract":
        max = Math.pow(10, Math.max(l1, l2));
        return (num1 * max - num2 * max) / max;
      case "multiply":
        return (Number(num1.toString().replace(".", "")) * Number(num2.toString().replace(".", ""))) / Math.pow(10, l1 + l2);
      case "divide":
        return operation(Number(num1.toString().replace(".", "")) / Number(num2.toString().replace(".", "")), Math.pow(10, l2 - l1), "multiply");
    }
  };
  let plus = (a, b) => {
    return operation(a, b, "plus");
  };
  let subtract = (a, b) => {
    return operation(a, b, "subtract");
  };
  let multiply = (a, b) => {
    return operation(a, b, "multiply");
  };
  let divide = (a, b) => {
    return operation(a, b, "divide");
  };
  return { plus, subtract, multiply, divide };
})();
 
console.log(0.1 + 0.2, calc.plus(0.1, 0.2));
//输出:0.30000000000000004 0.3
console.log(0.2 + 0.4, calc.plus(0.2, 0.4));
//输出:0.6000000000000001 0.6
console.log(1.0 - 0.9, calc.subtract(1.0, 0.9));
//输出:0.09999999999999998 0.1
console.log(0.9 - 0.7, calc.subtract(0.9, 0.7));
//输出:0.20000000000000007 0.2
console.log(19.9 * 100, calc.multiply(19.9, 100));
//输出:1989.9999999999998 1990
console.log(79.9 * 100, calc.multiply(79.9, 100));
//输出:7990.000000000001 7990
console.log(6.6 / 0.2, calc.divide(6.6, 0.2));
//输出:32.99999999999999 33
console.log(99.9 / 3, calc.divide(99.9, 3));
//输出:33.300000000000004 33.3

如果问题,请在评论区留言!


©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,794评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,050评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,587评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,861评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,901评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,898评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,832评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,617评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,077评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,349评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,483评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,199评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,824评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,442评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,632评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,474评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,393评论 2 352