zoukankan      html  css  js  c++  java
  • 自定义JS乘法运算误差解决!

    在实际开发中遇到这样一个乘法公式:数量*单价=总价

    像这样的浮点数列子:200*8.2,JS算出的结果是:

    像这种浮点数的乘法计算就会有误差,我们需要得到准确的值应该是:1640,与我们后台C#计算结果要一致。

    这里我们采用两种方式来解决。

    1:乘数和被乘数同时扩大10000倍。因为金额的最小单位也就是厘。然后结果在除以100000000,公式是:(2000000*82000)/100000000

      这种方式如果小数位要求越高,也还是有误差的,下面再说。

    2:自定义乘法,这种方式利用小学的计算公式,拆分计算,目的是避免float*float,而是使用int*float ,公式是:200*8.2 拆分就是:(200*8+200*0.2)+(0*8+0*0.2)

      具体实现代码如下:

      

      1 /**
      2 * 将小数拆分为整数+小数形式返回数组
      3 * @method
      4 * @param {String} 字符串数字
      5 * @return {Array} 返回数组 数组下表[0]部分为是否成功,数组[1]部分为整数,数组[2]为小数
      6 */
      7 function getDecimal(num) {
      8 var numArray = new Array();
      9 // 将输入的内容转为float类型(如果是小数为了保留小数部分)
     10 if (checkNumber(num)) {
     11 var isFloat = parseFloat(num);
     12 
     13 if (isFloat === 0) {
     14 numArray[0] = true;
     15 numArray[1] = parseFloat("0.0");// 整数部分
     16 numArray[2] = parseFloat("0.0");
     17 } else {
     18 // 是数字类型
     19 numArray[0] = true;
     20 //判断是否有小数
     21 if (isFloat.toString().indexOf(".") < 0) {
     22 numArray[1] = isFloat;// 整数部分
     23 numArray[2] = parseFloat("0.0");
     24 } else {
     25 var numArr = isFloat.toString().split(".");
     26 numArray[1] = parseInt(numArr[0]);// 整数部分
     27 numArray[2] = parseFloat('0.' + numArr[1]);// 小数部分
     28 }
     29 }
     30 } else {
     31 numArray[0] = false;
     32 }
     33 return numArray;
     34 }
     35 
     36 
     37 /**
     38 * 解决小数精度问题
     39 * @param {*数字 } a
     40 * @param {*数字 } b
     41 * @param {*符号 } sign
     42 * fixedFloat(0.3, 0.2, '-')
     43 * 参考:https://my.oschina.net/cjlice/blog/1616682
     44 */
     45 
     46 function fixedFloat(a, b, sign) {
     47 function handle(x) {
     48 var y = String(x);
     49 var p = y.lastIndexOf('.');
     50 if (p === -1) {
     51 return [y, 0];
     52 } else {
     53 return [y.replace('.', ''), y.length - p - 1];
     54 }
     55 }
     56 // v 操作数1, w 操作数2, s 操作符, t 精度
     57 function operate(v, w, s, t) {
     58 switch (s) {
     59 case '+':
     60 return (v + w) / t;
     61 case '-':
     62 return (v - w) / t;
     63 case '*':
     64 return (v * w) / (t * t);
     65 case '/':
     66 return (v / w);
     67 }
     68 }
     69 
     70 var c = handle(a);
     71 var d = handle(b);
     72 var k = 0;
     73 
     74 if (c[1] === 0 && d[1] === 0) {
     75 return operate(+c[0], +d[0], sign, 1);
     76 } else {
     77 k = Math.pow(10, Math.max(c[1], d[1]));
     78 if (c[1] !== d[1]) {
     79 if (c[1] > d[1]) {
     80 d[0] += padding0(c[1] - d[1]);
     81 } else {
     82 c[0] += padding0(d[1] - c[1]);
     83 }
     84 }
     85 return operate(+c[0], +d[0], sign, k);
     86 }
     87 }
     88 
     89 // 补0
     90 function padding0(p) {
     91 var z = '';
     92 while (p--) {
     93 z += '0';
     94 }
     95 return z;
     96 }
     97 
     98 //
     99 function plus(a, b) {
    100 return fixedFloat(a, b, '+');
    101 }
    102 //
    103 function minus(a, b) {
    104 return fixedFloat(a, b, '-');
    105 }
    106 //
    107 function multiply(a, b) {
    108 return fixedFloat(a, b, '*');
    109 }
    110 //
    111 function division(a, b) {
    112 return fixedFloat(a, b, '/');
    113 }
    114 
    115 
    116 /**
    117 * 自定义加法
    118 * @method
    119 * @param {String} 字符串
    120 * @param {String} 字符串
    121 * @return {number} 返回计算结果
    122 */
    123 function addition(numOne, numTwo) {
    124 return plus(numOne, numTwo);
    125 }
    126 
    127 /**
    128 * 自定义乘法
    129 * @method
    130 * @param {String} 字符串
    131 * @param {String} 字符串
    132 * @return {number} 返回计算结果
    133 */
    134 function multiplication(numOne, numTwo) {
    135 return multiply(numOne, numTwo);
    136 };
    137 
    138 
    139 /**
    140 * 判断输入是否是数字(包含小数正负)
    141 * @method
    142 * @param {String} 字符串
    143 * @return {true/false} 返回true/false
    144 */
    145 function checkNumber(theObj) {
    146 var reg = /^(\-|+)?d+(.d+)?$/;
    147 if (reg.test(theObj)) {
    148 return true;
    149 }
    150 return false;
    151 }
  • 相关阅读:
    CSP-J2019游记&解题报告
    旋转立方体实现
    博客背景线条实现
    垃圾基数排序
    链表实现队列(指针)
    公共子序列(luogu P1439)
    可并堆(左偏树)
    搜索(靶形数独)
    线段树(压位)luogu P1558色板游戏
    线段树区间取反
  • 原文地址:https://www.cnblogs.com/wendj/p/7906085.html
Copyright © 2011-2022 走看看