浮点数计算

2024-01-09 16:41:50

为什么 0.1 + 0.2 !== 0.3, 而 0.05 + 0.25 === 0.3

js在计算浮点数时可能不够准确,会产生舍入误差的问题,这是使用基于IEEE754二进制数值的浮点计算的通病,并非ECMAScript一家,其他使用相同数值格式的语言也存在这个问题。?

和其它语言如 Java 和 Python 不同,JavaScript 中所有数字包括整数和小数都只有一种类型:Number

使用 64 位固定长度来表示,也就是标准的double 双精度浮点数

这样的存储结构优点是可以归一化处理整数和小数,节省存储空间

64个比特又可分为三个部分,即:

第1位: 是符号的标志位(S), 0代表正数,1代表负数

第1-11位: 指数位(E), 存储指数(exponent),用来表示次方数

第12-63位: 尾数(M), 这52 位是尾数,超出的部分自动进一舍零

浮点数运算时, 通用的解决方案都是:

将小数转化为整数, 进行计算后, 再转化为小数就好了。

当然已经有成熟的工具库可以使用了, 例如Math.js, BigDecimal.js, number-precision等等

浮点数乘法运算

整数的乘法运算是准确的,这里我们将浮点数的乘法运算转化为整数乘法,然后除以10的他们小数位数之和次方

function FloatMul(arg1,arg2){
    var m=0,s1=arg1.toString(),s2=arg2.toString();
    try{
        m+=s1.split(".")[1].length
    }catch(e){}
    try{
        m+=s2.split(".")[1].length
    }catch(e){}
    return Number(s1.replace(".",""))*Number(s2.replace(".",""))/Math.pow(10,m)
}
 

浮点数加法运算

这里我们将浮点数分别乘以小数位最大的之后,再相加,然后除以10的小数位数最大的次方?

function FloatAdd(arg1,arg2){
    var r1,r2,m;
    try{
        r1=arg1.toString().split(".")[1].length
    }catch(e){
        r1=0
    }
    try{
        r2=arg2.toString().split(".")[1].length
    }catch(e){
        r2=0
    }
    m=Math.pow(10,Math.max(r1,r2))
    return (arg1*m+arg2*m)/m
}

浮点数减法运算

function FloatSub(arg1,arg2){
    var r1,r2,m,n;
    try{
        r1=arg1.toString().split(".")[1].length
    }catch(e){
        r1=0
    }
    try{
        r2=arg2.toString().split(".")[1].length
    }catch(e){
        r2=0
    }
    m=Math.pow(10,Math.max(r1,r2));
    //动态控制精度长度
    n=(r1>=r2)?r1:r2;
    return ((arg1*m-arg2*m)/m).toFixed(n);
}

除法运算

?类似加法,将两个数各自乘以10的他们小数位最大的次方,再相除,然后乘以1e(除数小数位 - 被除数小数位)

function FloatDiv(arg1,arg2){
    var t1=0,t2=0,r1,r2;
    try{
        t1=arg1.toString().split(".")[1].length
    }catch(e){}
    try{
        t2=arg2.toString().split(".")[1].length
    }catch(e){}
    with(Math){
        r1=Number(arg1.toString().replace(".",""))
        r2=Number(arg2.toString().replace(".",""))
        return (r1/r2)*pow(10,t2-t1);
    }
}

超大数计算

整数运算在数字特别大的时候,计算结果的尾数也会不准确。如超过9个9乘以本身的乘法运算时,计算出的结果最大是999999998000000000,正常结果个位数应该为1,但是js运算结果将个位数去掉了,类似数据比较大时,经验证,7位数乘以7位数还是正常的,超过后就不准确了。

JS 提供Number.MAX_SAFE_INTEGER常量来表示 最大安全整数,Number.MIN_SAFE_INTEGER常量表示最小安全整数:

const minInt = Number.MIN_SAFE_INTEGER;

console.log(minInt);         // → -9007199254740991

console.log(minInt - 5);     // → -9007199254740996

// notice how this outputs the same value as above
console.log(minInt - 4);     // → -9007199254740996

使用BigInt

BigInt是ECMAScript 2020的新功能之一,它可以处理超过JavaScript Number类型最大值的大数字,最大的范围是2^53 - 1,超过这个范围的数字就会被自动转换为科学计数法。使用BigInt即可轻松处理大数字。

BigInt可以单独作为一种类型使用,也可以通过在数字后面添加n的方式进行创建。例如:

const?bigNumber?=?123456789123456789n;

// 定义两个超大数字
const num1 = BigInt(9007199254740993); // 最大安全整数
const num2 = BigInt("12345678901234567890"); // 更大的超大数字
 
// 加法运算
console.log(num1 + num2); // 输出结果为 "12345678901234567893"
 
// 乘法运算
console.log(num1 * num2); // 输出结果为 "11234567890123456789000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

不能使用严格相等运算符将BigInt与常规数字进行比较,因为它们的类型不同

console.log(10n === 10);    // → false

console.log(typeof 10n);    // → bigint
console.log(typeof 10);     // → number

可以使用等号运算符,它在处理操作数之前执行隐式类型转换

console.log(10n == 10);    // → true

不允许在bigint和 Number 之间进行混合操作,因为隐式类型转换可能丢失信息

10n + 1;    // Uncaught TypeError: Cannot mix BigInt and other types, use explicit conversions
Math.max(2n, 4n, 6n);    // TypeError...

当使用 BigInt 时,带小数的运算会被取整。?

// 2n
const expected = 4n / 2n;

// 2n, not 2.5n
const rounded = 5n / 2n;

?BigInt转为String时,其标志性的n会被省略

String(10n);    // '10'
'' + 11n;    // '11'

使用第三方库

bignumber.js

decimal.js

文章来源:https://blog.csdn.net/array__/article/details/135393927
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。