我有以下虚拟测试脚本:

函数测试(){变量x=0.1*0.2;document.write(x);}测试();

这将打印结果0.020000000000000004,而它应该只打印0.02(如果您使用计算器)。据我所知,这是由于浮点乘法精度的错误。

有没有人有一个好的解决方案,在这种情况下,我得到了正确的结果0.02?我知道还有一些函数,比如toFixed或舍入,这是另一种可能,但我真的希望在不进行任何切割和舍入的情况下打印整个数字。我只是想知道你们中的一个人是否有一些好的、优雅的解决方案。

当然,否则我会舍入到10位数左右。


当前回答

优雅、可预测、可重复使用

让我们以一种优雅的、可重用的方式来处理这个问题。通过在数字、公式或内置Math函数的末尾添加.dedecimal,以下七行将允许您访问任意数字所需的浮点精度。

//首先扩展本机Number对象以处理精度。这将填充//所有数学运算的功能。Object.defineProperty(Number.prototype,“decimal”{get:函数decimal(){Number.precision=数字中的“精度”?数字精度:3;var f=数学.pow(10,数字精度);return Math.round(this*f)/f;}});//现在让我们来看看它是如何通过调整我们的全球精度水平和//检查我们的结果。console.log(“'1/3+1/3+1/3=1'对吗?”);console.log((0.3333+0.3333+0.3333).decimal==1);//真的console.log(0.3333.decimal);//0.333-一个原始的4位小数,修剪为3。。。数字精度=3;console.log(“精度:3”);console.log((0.8+0.2).dedecimal);//1.console.log((0.08+0.02).decimal);//0.1控制台日志((0.008+0.002)十进制);//0.01console.log((0.0008+0.0002).decimal);//0.001数字精度=2;console.log(“精度:2”);console.log((0.8+0.2).dedecimal);//1.console.log((0.08+0.02).decimal);//0.1控制台日志((0.008+0.002)十进制);//0.01console.log((0.0008+0.0002).decimal);//0数字精度=1;console.log(“精度:1”);console.log((0.8+0.2).dedecimal);//1.console.log((0.08+0.02).decimal);//0.1控制台日志((0.008+0.002)十进制);//0console.log((0.0008+0.0002).decimal);//0数字精度=0;console.log(“精度:0”);console.log((0.8+0.2).dedecimal);//1.console.log((0.08+0.02).decimal);//0控制台日志((0.008+0.002)十进制);//0console.log((0.0008+0.0002).decimal);//0

干杯

其他回答

从我的角度来看,这里的想法是将fp数舍入,以便获得一个漂亮/简短的默认字符串表示。

53位有效位精度提供15到17位有效小数位数精度(2−53≈1.11×10−16)。如果具有最多15个有效数字的十进制字符串被转换为IEEE 754双精度表示,然后转换回具有相同位数的十进制字符串,最终结果应与原始字符串匹配。如果IEEE 754双精度数字被转换为具有至少17个有效数字的十进制字符串,然后转换回双精度表示,最终结果必须与原始数字匹配。...由于分数(F)有效位的52位出现在内存格式中,因此总精度为53位(约16位小数,53 log10(2)≈15.955)。。。维基百科

(0.1).toPrecision(100) ->
0.1000000000000000055511151231257827021181583404541015625000000000000000000000000000000000000000000000

(0.1+0.2).toPrecision(100) ->
0.3000000000000000444089209850062616169452667236328125000000000000000000000000000000000000000000000000

然后,据我所知,我们可以将值四舍五入到15位,以保持良好的字符串表示。

10**Math.floor(53 * Math.log10(2)) // 1e15

eg.

Math.round((0.2+0.1) * 1e15 ) / 1e15
0.3
(Math.round((0.2+0.1) * 1e15 ) / 1e15).toPrecision(100)
0.2999999999999999888977697537484345957636833190917968750000000000000000000000000000000000000000000000

功能如下:

function roundNumberToHaveANiceDefaultStringRepresentation(num) {

    const integerDigits = Math.floor(Math.log10(Math.abs(num))+1);
    const mult = 10**(15-integerDigits); // also consider integer digits
    return Math.round(num * mult) / mult;
}

要处理任意浮点数,请执行以下操作:

function shorten(num) {
    num += 0.000000001;// to deal with "12.03999999997" form
    num += '';
    return num.replace(/(\.\d*?)0{5,}\d+$/, '$1') * 1;
}

console.log(1.2+1.9===1.3+1.8);// false
console.log(shorten(1.2+1.9)===shorten(1.3+1.8));// true

当添加两个浮点值时,它永远不会给出精确的值,因此我们需要将其固定为有助于我们进行比较的某个数字。

console.log((parseFloat(0.1)+parseFlat(0.2)).toFixed(1)==parseFloat(0.3).toFixed(1));

使用以下功能输出:

var toFixedCurrency = function(num){
    var num = (num).toString();
    var one = new RegExp(/\.\d{1}$/).test(num);
    var two = new RegExp(/\.\d{2,}/).test(num);
    var result = null;

    if(one){ result = num.replace(/\.(\d{1})$/, '.$10');
    } else if(two){ result = num.replace(/\.(\d{2})\d*/, '.$1');
    } else { result = num*100; }

    return result;
}

function test(){
    var x = 0.1 * 0.2;
    document.write(toFixedCurrency(x));
}

test();

注意FixedCurrency(x)的输出。

您正在寻找JavaScript的sprintf实现,这样您就可以以期望的格式写出带有小错误的浮点数(因为它们是以二进制格式存储的)。

试试javascript sprintf,你可以这样称呼它:

var yourString = sprintf("%.2f", yourNumber);

将数字打印为带有两个小数点的浮点数。

如果您不希望仅为浮点舍入到给定精度而包含更多文件,也可以使用Number.toFixed()进行显示。