我有以下虚拟测试脚本:

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

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

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

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


当前回答

我喜欢佩德罗·拉达里亚的解决方案,并使用类似的方法。

function strip(number) {
    return (parseFloat(number).toPrecision(12));
}

与Pedros解决方案不同,这将向上取整0.999……重复,并精确到最低有效位数的正负一。

注意:当处理32位或64位浮点时,应该使用toPrecision(7)和toPrecision(15)以获得最佳结果。有关原因的信息,请参阅此问题。

其他回答

我找不到使用内置的Number.EPSILON来解决这类问题的解决方案,因此我的解决方案如下:

function round(value, precision) {
  const power = Math.pow(10, precision)
  return Math.round((value*power)+(Number.EPSILON*power)) / power
}

这使用1和大于1的最小浮点数之间的已知最小差值来修正EPSILON舍入误差,结果仅比舍入阈值低一个EPSILON。

64位浮点的最大精度为15,32位浮点的最高精度为6。您的javascript可能是64位。

从我的角度来看,这里的想法是将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;
}

你只需要决定你实际想要多少个小数位数-不能既吃蛋糕又吃蛋糕:-)

随着每一次进一步的操作,数值误差都会累积,如果你不及早切断它,它只会增长。数值库显示的结果看起来很干净,只需在每一步中删除最后2位数字,数值协处理器也有“正常”和“完整”长度,原因相同。对于一个处理器来说,截断是便宜的,但对于脚本(乘法、除法和使用pov(…))来说非常昂贵。好的数学库将提供floor(x,n)来为您进行截断。

因此,至少您应该使用pov(10,n)使全局var/常量-这意味着您决定了所需的精度:-)然后执行:

Math.floor(x*PREC_LIM)/PREC_LIM  // floor - you are cutting off, not rounding

你也可以继续做数学运算,只在最后截止——假设你只显示结果而不做if-s。如果你能做到这一点,那么.toFixed(…)可能会更有效率。

如果您正在进行If-s/比较,并且不想截断,那么还需要一个小常数,通常称为eps,它比最大预期误差高一个小数位。假设你的截止值是最后两位小数,那么你的eps在倒数第三位(第三位最不重要)有1,你可以用它来比较结果是否在预期的eps范围内(0.02-eps<0.1*0.2<0.02+eps)。

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

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

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

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

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

0.6*3太棒了!)对我来说,这很好:

function dec( num )
{
    var p = 100;
    return Math.round( num * p ) / p;
}

非常简单))