如何处理JavaScript中的浮点数精度?

时间:2009-09-22 07:34:42

标签: javascript floating-point

我有以下虚拟测试脚本:

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

这将打印结果0.020000000000000004,同时打印0.02(如果您使用计算器)。据我所知,这是由于浮点乘法精度的误差造成的。

有没有人有一个好的解决方案,以便在这种情况下我得到正确的结果0.02?我知道有toFixed这样的函数或舍入将是另一种可能性,但我想真正打印整个数字而不进行任何切割和舍入。只是想知道你们其中一个人是否有一些漂亮,优雅的解决方案。

当然,否则我会大约10位左右。

45 个答案:

答案 0 :(得分:417)

来自Floating-Point Guide

  

我该怎么做才能避免这个问题?

     

这取决于什么样的   你正在做的计算。

     
      
  • 如果你真的需要你的结果准确加起来,特别是当你   使用金钱:使用特殊小数   数据类型。
  •   
  • 如果你只是不想看到所有这些额外的小数位:简单地说   将结果格式化为固定的   小数位数   显示它。
  •   
  • 如果您没有可用的十进制数据类型,则可以使用另一种方法   用整数,例如做钱   完全以美分计算。但   这是更多的工作,并有一些   缺点。
  •   

请注意,第一点仅适用于您确实需要特定的精确十进制行为。大多数人不需要这样,他们只是因为他们的程序无法正确使用1/10这样的数字而没有意识到如果它以1/3发生它们甚至不会出现相同的错误。

如果第一点确实适用于您,请使用BigDecimal for JavaScript,这根本不是优雅的,但实际上解决了问题,而不是提供不完美的解决方法。

答案 1 :(得分:106)

我喜欢Pedro Ladaria的解决方案并使用类似的东西。

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

与Pedros解决方案不同,这将重复0.999 ...重复,并且在最低有效数字上精确到正负1。

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

答案 2 :(得分:62)

对于数学上倾向的:http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html

推荐的方法是使用校正因子(乘以10的合适幂,以便算术在整数之间发生)。例如,在0.1 * 0.2的情况下,修正系数为10,您正在执行计算:

> var x = 0.1
> var y = 0.2
> var cf = 10
> x * y
0.020000000000000004
> (x * cf) * (y * cf) / (cf * cf)
0.02

(非常快)解决方案看起来像:

var _cf = (function() {
  function _shift(x) {
    var parts = x.toString().split('.');
    return (parts.length < 2) ? 1 : Math.pow(10, parts[1].length);
  }
  return function() { 
    return Array.prototype.reduce.call(arguments, function (prev, next) { return prev === undefined || next === undefined ? undefined : Math.max(prev, _shift (next)); }, -Infinity);
  };
})();

Math.a = function () {
  var f = _cf.apply(null, arguments); if(f === undefined) return undefined;
  function cb(x, y, i, o) { return x + f * y; }
  return Array.prototype.reduce.call(arguments, cb, 0) / f;
};

Math.s = function (l,r) { var f = _cf(l,r); return (l * f - r * f) / f; };

Math.m = function () {
  var f = _cf.apply(null, arguments);
  function cb(x, y, i, o) { return (x*f) * (y*f) / (f * f); }
  return Array.prototype.reduce.call(arguments, cb, 1);
};

Math.d = function (l,r) { var f = _cf(l,r); return (l * f) / (r * f); };

在这种情况下:

> Math.m(0.1, 0.2)
0.02

我绝对建议使用像SinfulJS

这样经过测试的库

答案 3 :(得分:39)

你只是在进行乘法运算吗?如果是这样,那么你可以利用一个关于十进制算术的巧妙秘密。那是NumberOfDecimals(X) + NumberOfDecimals(Y) = ExpectedNumberOfDecimals。也就是说,如果我们有0.123 * 0.12,那么我们知道将有5个小数位,因为0.123有3个小数位,0.12有2个小数位。因此,如果JavaScript为我们提供了类似0.014760000002的数字,我们可以安全地舍入到小数点后第五位,而不必担心会失去精确度。

答案 4 :(得分:24)

您正在寻找JavaScript的sprintf实现,以便您可以用您期望的格式写出包含小错误的浮点数(因为它们以二进制格式存储)。

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

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

将您的号码打印为带有两位小数的浮点数。

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

答案 5 :(得分:16)

我发现BigNumber.js符合我的需求。

  
    

用于任意精度十进制和非十进制算术的JavaScript库。

  

它很好documentation,作者非常勤奋地回应反馈。

同一作者还有2个其他类似的库:

Big.js

  
    

用于任意精度十进制算术的小型,快速JavaScript库。 bignumber.js的小妹妹。

  

Decimal.js

  
    

JavaScript的任意精度十进制类型。

  

以下是使用BigNumber的一些代码:

$(function(){

  
  var product = BigNumber(.1).times(.2);  
  $('#product').text(product);

  var sum = BigNumber(.1).plus(.2);  
  $('#sum').text(sum);


});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

<!-- 1.4.1 is not the current version, but works for this example. -->
<script src="http://cdn.bootcss.com/bignumber.js/1.4.1/bignumber.min.js"></script>

.1 &times; .2 = <span id="product"></span><br>
.1 &plus; .2 = <span id="sum"></span><br>

答案 6 :(得分:14)

此函数将根据两个浮点数的乘法确定所需的精度,并以适当的精度返回结果。优雅虽然不是。

function multFloats(a,b){
  var atens = Math.pow(10,String(a).length - String(a).indexOf('.') - 1), 
      btens = Math.pow(10,String(b).length - String(b).indexOf('.') - 1); 
  return (a * atens) * (b * btens) / (atens * btens); 
}

答案 7 :(得分:14)

var times = function (a, b) {
    return Math.round((a * b) * 100)/100;
};

--- ---或

var fpFix = function (n) {
    return Math.round(n * 100)/100;
};

fpFix(0.1*0.2); // -> 0.02

--- ---也

var fpArithmetic = function (op, x, y) {
    var n = {
            '*': x * y,
            '-': x - y,
            '+': x + y,
            '/': x / y
        }[op];        

    return Math.round(n * 100)/100;
};

---如---

fpArithmetic('*', 0.1, 0.2);
// 0.02

fpArithmetic('+', 0.1, 0.2);
// 0.3

fpArithmetic('-', 0.1, 0.2);
// -0.1

fpArithmetic('/', 0.2, 0.1);
// 2

答案 8 :(得分:11)

你只需要决定你真正想要多少个十进制数字 - 不能吃蛋糕并吃掉它: - )

数值误差随着每一次进一步的操作而累积,如果你不早点切断,它就会增长。呈现干净的结果的数值库在每一步都简单地切断了最后两位数字,数字协处理器也具有“正常”和“完整”长度,原因相同。 Cuf-offs对于处理器来说很便宜,但对于脚本来说非常昂贵(乘法和分割以及使用pov(...))。好的数学库会提供楼层(x,n)来为你做截止。

所以至少你应该用pov(10,n)创建全局变量/常量 - 这意味着你决定了你需要的精度:-)然后做:

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

你也可以继续做数学并且只在最后切断 - 假设你只是显示而不是if-s结果。如果你能做到这一点,那么.toFixed(...)可能会更有效率。

如果你正在进行if-s /比较并且不想削减那么你还需要一个小的常量,通常称为eps,它比最大预期误差高一位小数。假设您的截止时间是最后两位小数 - 那么您的eps在最后一位(第三位最低位)的第3位有1,您可以使用它来比较结果是否在预期的eps范围内(0.02 -ss&lt; 0.1 * 0.2 <0.02 + eps。。

答案 9 :(得分:9)

phpjs.org的round()函数效果很好:http://phpjs.org/functions/round

num = .01 + .06;  // yields 0.0699999999999
rnum = round(num,12); // yields 0.07

答案 10 :(得分:7)

令人惊讶的是,此功能尚未发布,尽管其他功能也有类似的变化。它来自Math.round()的MDN Web文档。 它简洁,允许不同的精度。

function precisionRound(number, precision) {
  var factor = Math.pow(10, precision);
  return Math.round(number * factor) / factor;
}

console.log(precisionRound(1234.5678,1));    //预期产量:1234.6

console.log(precisionRound(1234.5678,-1));    //预期产量:1230

var inp = document.querySelectorAll('input');
var btn = document.querySelector('button');

btn.onclick = function(){
  inp[2].value = precisionRound( parseFloat(inp[0].value) * parseFloat(inp[1].value) , 5 );
};

//MDN function
function precisionRound(number, precision) {
  var factor = Math.pow(10, precision);
  return Math.round(number * factor) / factor;
}
button{
display: block;
}
<input type='text' value='0.1'>
<input type='text' value='0.2'>
<button>Get Product</button>
<input type='text'>

答案 11 :(得分:6)

您获得的结果在不同语言,处理器和操作系统的浮点实现中是正确且相当一致的 - 唯一改变的是浮点实际上是双倍(或更高)时的不准确程度。

0.1的二进制浮点数就像十进制的1/3(即0.3333333333333 ......永远),没有准确的方法来处理它。

如果你正在处理浮动总是期望小的舍入错误,那么你也总是必须将显示的结果舍入到合理的位置。作为回报,您将获得非常快速和强大的算术,因为所有计算都在处理器的本机二进制文件中。

大多数情况下,解决方案不是切换到定点运算,主要是因为它更慢,99%的时间你不需要精度。如果你正在处理需要那种准确度的东西(比如金融交易),那么Javascript可能不是最好的工具(因为你想强制执行定点类型,静态语言可能更好) )。

你正在寻找优雅的解决方案然后我担心这就是它:浮动很快但是有小的舍入错误 - 在显示结果时总是圆润到合理的东西。

答案 12 :(得分:6)

要避免这种情况,您应该使用整数值而不是浮点数。因此,当您希望2个位置精度使用值* 100时,3个位置使用1000.显示时使用格式化程序放入分隔符。

许多系统都忽略了这种方式使用小数。这就是为什么许多系统使用美分(作为整数)而不是美元/欧元(作为浮点数)的原因。

答案 13 :(得分:5)

如果您想为小型操作绕过此问题,可以使用parseFloat()toFixed()

a = 0.1;
b = 0.2;

a + b = 0.30000000000000004;

c = parseFloat((a+b).toFixed(2));

c = 0.3;

a = 0.3;
b = 0.2;

a - b = 0.09999999999999998;

c = parseFloat((a-b).toFixed(2));

c = 0.1;

答案 14 :(得分:5)

0.6 * 3真棒!)) 对我来说这很好用:

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

非常非常简单))

答案 15 :(得分:5)

<强>问题

浮点数不能精确存储所有十进制值。因此,当使用浮点格式时,输入值将始终存在舍入误差。 输入上的错误当然会导致输出错误。 在离散函数或运算符的情况下,函数或运算符离散的点周围的输出可能存在很大差异。

浮点值的输入和输出

因此,在使用浮点变量时,您应该始终注意这一点。并且在显示之前,您应该始终对浮点数计算所需的输出进行格式化/调节 当仅使用连续函数和运算符时,通常会舍入到所需的精度(不要截断)。用于将浮点数转换为字符串的标准格式设置功能通常会为您执行此操作 由于舍入会增加一个错误,导致总误差超过所需精度的一半,因此应根据输入的预期精度和所需的输出精度来校正输出。你应该

  • 将输入舍入到预期的精度,或确保不能以更高的精度输入任何值。
  • 在舍入/格式化之前向输出添加一个小值,该值小于或等于所需精度的1/4,并且大于输入和计算期间的舍入误差引起的最大预期误差。如果不可能,则所用数据类型的精度组合不足以为计算提供所需的输出精度。

这两件事通常都没有完成,在大多数情况下,由于没有这样做而导致的差异太小而不适合大多数用户,但我已经有一个项目,其中输出没有被用户接受校正。

离散函数或运算符(如modula)

当涉及离散运算符或函数时,可能需要额外的更正以确保输出符合预期。在舍入之前舍入并添加小的修正不能解决问题 在应用离散函数或运算符之后,可能需要对中间计算结果进行特殊检查/校正。 对于特定情况(modula运算符),请参阅我的问题答案:Why does modulus operator return fractional number in javascript?

最好避免出现问题

通过使用数据类型(整数或定点格式)进行这样的计算来避免这些问题通常更有效,这样可以存储预期的输入而不会出现舍入错误。 例如,您不应该使用浮点值进行财务计算。

答案 16 :(得分:3)

看看Fixed-point arithmetic。如果您想要操作的数字范围很小(例如货币),它可能会解决您的问题。我会把它四舍五入到几个十进制值,这是最简单的解决方案。

答案 17 :(得分:3)

enter image description here

    You can use library https://github.com/MikeMcl/decimal.js/. 
    it will   help  lot to give proper solution. 
    javascript console output 95 *722228.630 /100 = 686117.1984999999
    decimal library implementation 
    var firstNumber = new Decimal(95);
    var secondNumber = new Decimal(722228.630);
    var thirdNumber = new Decimal(100);
    var partialOutput = firstNumber.times(secondNumber);
    console.log(partialOutput);
    var output = new Decimal(partialOutput).div(thirdNumber);
    alert(output.valueOf());
    console.log(output.valueOf())== 686117.1985

答案 18 :(得分:3)

请注意,对于通用用途,此行为可能是可以接受的 在比较这些浮点值以确定适当的操作时会出现问题    随着ES6的出现,定义了一个新的常量import sys class Glob101: def Func101(arg0, arg1): sys.stdout.write("Func101 ARG1 :" + arg0 + ": ARG1 :"+ arg1 + ":\n"); getattr(globals()['Glob101'], 'Func101')('This Should Work', 'I believe !!!'); 来确定可接受的误差范围:
所以不要像这样进行比较

Number.EPSILON

您可以定义自定义比较功能,如下所示:

0.1 + 0.2 === 0.3 // which returns false

来源:http://2ality.com/2015/04/numbers-math-es6.html#numberepsilon

答案 19 :(得分:3)

您无法使用二进制浮点类型(ECMAScript用于表示浮点值)精确表示大多数小数部分。因此,除非您使用任意精度算术类型或基于十进制的浮点类型,否则没有一个优雅的解决方案。例如,the Calculator app that ships with Windows now uses arbitrary precision arithmetic to solve this problem

答案 20 :(得分:3)

尝试我的辣椒算术库,你可以看到here。 如果你想要更高版本,我可以给你一个。

答案 21 :(得分:2)

我对mod 3有一个令人讨厌的舍入错误问题。有时当我得到0时,我会得到.000 ... 01。这很容易处理,只需测试&lt; = .01。但有时我会得到2.99999999999998。哎哟!

BigNumbers解决了这个问题,但引入了另一个有点讽刺意味的问题。当试图将8.5加载到BigNumbers时,我被告知它确实是8.4999 ...并且有超过15位有效数字。这意味着BigNumbers无法接受它(我相信我提到这个问题有些讽刺)。

解决具有讽刺意味的问题的简单方法:

x = Math.round(x*100);
// I only need 2 decimal places, if i needed 3 I would use 1,000, etc.
x = x / 100;
xB = new BigNumber(x);

答案 22 :(得分:1)

decimal.jsbig.jsbignumber.js可用于避免Javascript中的浮点操作问题:

0.1 * 0.2                                // 0.020000000000000004
x = new Decimal(0.1)
y = x.times(0.2)                          // '0.2'
x.times(0.2).equals(0.2)                  // true
  

big.js:极简主义;易于使用;小数点后指定的精度;精度仅适用于除法。

     

bignumber.js:以2-64为基数;配置选项; NaN;无穷;小数点后指定的精度;精度仅适用于除法;基本前缀。

     

decimal.js:以2-64为基数;配置选项; NaN;无穷;非整数幂,exp,ln,log;有效位数指定的精度;始终应用精度;随机数。

link to detailed comparisons

答案 23 :(得分:1)

使用

var x = 0.1*0.2;
 x =Math.round(x*Math.pow(10,2))/Math.pow(10,2);

答案 24 :(得分:1)

使用数字(1.234443).toFixed(2);它将打印1.23

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

答案 25 :(得分:1)

你是对的,原因是浮点数的精度有限。将有理数存储为两个整数的除法,在大多数情况下,您将能够存储数字而不会出现任何精度损失。在打印时,您可能希望将结果显示为分数。通过我提出的表述,它变得微不足道。

当然,这对无理数不会有太大帮助。但您可能希望以导致最少问题的方式优化计算(例如,检测sqrt(3)^2)等情况。

答案 26 :(得分:1)

不优雅,但完成工作(删除尾随零)

var num = 0.1*0.2;
alert(parseFloat(num.toFixed(10))); // shows 0.02

答案 27 :(得分:0)

我找不到使用内置 Number.EPSILON 的解决方案来帮助解决此类问题,所以这是我的解决方案:

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

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

最大精度为 15 for 64bit 浮点数和 6 for 32bit 浮点数。您的 javascript 可能是 64 位。

答案 28 :(得分:0)

我通常使用这样的东西。

function pf(n) {
    return Math.round(n * 1e15) / 1e15;
}

我并不声称这在任何方面都是最佳的,但我喜欢它的简单性。它将数字四舍五入到小数点后 15 位左右。我没有目睹它返回不准确的浮点数,但奇怪的是当我在最后使用 * 1e-15 时它已经这样做了,但没有使用这种方法。

此解决方案可能更适合随意使用——而不是精确的数学用途——其中精度错误会扰乱您的代码。

答案 29 :(得分:0)

我喜欢带有修正系数的方法,这里是我对 ES6 和 ES5 标准的简短决定。与 toFixed 方法相比,它的优势在于它不会在数字的末尾留下不必要的零,如果我们想要四舍五入到数百,但结果数字是十分之一:

ES6 变体:

// .1 + .2
((a,b,crr) => (a*crr + b*crr)/crr)(.1,.2,100/*correction factor*/);//0.3
// .1 * .2
((a,b,crr) => a*crr*b/crr)(.1,.2,100);//0.02

ES5 变体:

// .1 + .2
(function(a,b,crr){ return (a*crr + b*crr)/crr; })(.1,.2,100/*correction factor*/);//0.3
// .1 * .2
(function(a,b,crr){ return a*crr*b/crr; })(.1,.2,100);//0.02

答案 30 :(得分:0)

根据我喜欢的@SheetJs的答案,将它们组合在一起:

  getCorrectionFactor(numberToCheck: number): number {
    var correctionFactor: number = 1;
    
    if (!Number.isInteger(numberToCheck)) {
      while (!Number.isInteger(numberToCheck)) {
        correctionFactor *= 10;
        numberToCheck *= correctionFactor;
      }
    }

    return correctionFactor;
  }

答案 31 :(得分:0)

从我的角度来看,这里的想法是对fp值进行四舍五入,以便获得漂亮/简短的默认字符串表示形式。

  

53位有效位数精度为15到17个有效十进制数字精度(2-53≈1.11×10-16)。   如果将最多15个有效数字的十进制字符串转换为IEEE 754双精度表示形式,   然后转换回具有相同位数的十进制字符串,最终结果应与原始字符串匹配。   如果将IEEE 754双精度数字转换为具有至少17个有效数字的十进制字符串,   然后转换回双精度表示形式,则最终结果必须与原始数字匹配。
  ...
  由于52位有效分数(F)以内存格式出现,因此总精度为53位(大约16个十进制数字,即53 log10(2)≈15.955)。这些位的布局如下... wikipedia

(0.1).toPrecision(100) ->
0.1000000000000000055511151231257827021181583404541015625000000000000000000000000000000000000000000000

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

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

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

例如

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;
}

答案 32 :(得分:0)

我一直在寻找相同的解决方案,但我得出的结论是,如果您将整数添加为1,然后评估该console.log(0.1 * 0.2 + 1);。结果为1.02。这可用于将原始x变量舍入到正确的数量。

在您的示例中,一旦检索到小数位数2的长度,我们就可以将其与toFixed()函数一起使用,以正确地对原始x变量进行四舍五入。

请在注释部分中查看代码内部有关此功能的作用。

var myX= 0.2 * 0.1;
var myX= 42.5-42.65;
var myX= 123+333+3.33+33333.3+333+333;

console.log(myX);
// Outputs (example 1): 0.020000000000000004
// Outputs (example 2): -0.14999999999999858
// Outputs (example 3): 34458.630000000005
// Wrong

function fixRoundingError(x) {
// 1. Rounds to the nearest 10
//    Also adds 1 to round of the value in some other cases, original x variable will be used later on to get the corrected result.
var xRound = eval(x.toFixed(10)) + 1;
// 2. Using regular expression, remove all digits up until the decimal place of the corrected equation is evaluated..
var xDec = xRound.toString().replace(/\d+\.+/gm,'');
// 3. Gets the length of the decimal places.
var xDecLen = xDec.length;
// 4. Uses the original x variable along with the decimal length to fix the rounding issue.
var x = eval(x).toFixed(xDecLen);
// 5. Evaluate the new x variable to remove any unwanted trailing 0's should there be any.
return eval(x);
}

console.log(fixRoundingError(myX));
// Outputs (example 1): 0.02
// Outputs (example 2): -0.15
// Outputs (example 3): 34458.63
// Correct

在我尝试过的每种情况下,它都会返回与Windows中的计算器相同的值,并且如果自动出现尾随的0,还会舍入结果。

答案 33 :(得分:0)

首先将两个数字都设为整数,然后执行表达式,然后将结果除以小数点后即可解决:

function evalMathematicalExpression(a, b, op) {
    const smallest = String(a < b ? a : b);
    const factor = smallest.length - smallest.indexOf('.');

    for (let i = 0; i < factor; i++) {
        b *= 10;
        a *= 10;
    }

    a = Math.round(a);
    b = Math.round(b);
    const m = 10 ** factor;
    switch (op) {
        case '+':
            return (a + b) / m;
        case '-':
            return (a - b) / m;
        case '*':
            return (a * b) / (m ** 2);
        case '/':
            return a / b;
    }

    throw `Unknown operator ${op}`;
}

几个操作的结果(排除的数字是来自eval的结果):

0.1 + 0.002   = 0.102 (0.10200000000000001)
53 + 1000     = 1053 (1053)
0.1 - 0.3     = -0.2 (-0.19999999999999998)
53 - -1000    = 1053 (1053)
0.3 * 0.0003  = 0.00009 (0.00008999999999999999)
100 * 25      = 2500 (2500)
0.9 / 0.03    = 30 (30.000000000000004)
100 / 50      = 2 (2)

答案 34 :(得分:0)

在使用整数操作期间,避免处理浮点数

如到目前为止投票最多的答案所述,您可以使用整数,这意味着将您要使用的每个小数的所有因子乘以10,然后将结果除以使用相同的数字。

例如,如果要使用2个小数,则在执行该操作之前,将所有因子乘以100,然后将结果除以100。

这里是一个示例,Result1是常规结果,Result2使用解决方案:

var Factor1="1110.7";
var Factor2="2220.2";
var Result1=Number(Factor1)+Number(Factor2);
var Result2=((Number(Factor1)*100)+(Number(Factor2)*100))/100;
var Result3=(Number(parseFloat(Number(Factor1))+parseFloat(Number(Factor2))).toPrecision(2));
document.write("Result1: "+Result1+"<br>Result2: "+Result2+"<br>Result3: "+Result3);

第三个结果是显示代替使用parseFloat会发生什么,这在我们的案例中造成了冲突。

答案 35 :(得分:0)

如果您不想每次都考虑调用函数,可以创建一个为您处理转换的类。

class Decimal {
  constructor(value = 0, scale = 4) {
    this.intervalValue = value;
    this.scale = scale;
  }

  get value() {
    return this.intervalValue;
  }

  set value(value) {
    this.intervalValue = Decimal.toDecimal(value, this.scale);
  }

  static toDecimal(val, scale) {
    const factor = 10 ** scale;
    return Math.round(val * factor) / factor;
  }
}

用法:

const d = new Decimal(0, 4);
d.value = 0.1 + 0.2;              // 0.3
d.value = 0.3 - 0.2;              // 0.1
d.value = 0.1 + 0.2 - 0.3;        // 0
d.value = 5.551115123125783e-17;  // 0
d.value = 1 / 9;                  // 0.1111

当然,在处理Decimal时有一些警告:

d.value = 1/3 + 1/3 + 1/3;   // 1
d.value -= 1/3;              // 0.6667
d.value -= 1/3;              // 0.3334
d.value -= 1/3;              // 0.0001

理想情况下,您希望使用高倍数(例如12),然后在需要展示或将其存储在某个地方时将其转换为向下。就我个人而言,我确实尝试过创建UInt8Array并尝试创建一个精度值(与SQL Decimal类型非常相似),但是由于Javascript不允许您重载运算符,因此无法使用基本的数学运算符有点麻烦。 (+-/*)并使用类似add()substract()mult()之类的函数。对于我的需求,这是不值得的。

但是,如果您确实需要这种精度,并且愿意忍受数学函数的使用,那么我建议使用decimal.js库。

答案 36 :(得分:0)

我对编程并不擅长,但对这个主题非常感兴趣,所以我试着理解如何在不使用任何库或脚本的情况下解决这个问题

我在暂存器上写了这个

var toAlgebraic = function(f1, f2) {
    let f1_base = Math.pow(10, f1.split('.')[1].length);
    let f2_base = Math.pow(10, f2.split('.')[1].length);
    f1 = parseInt(f1.replace('.', ''));
    f2 = parseInt(f2.replace('.', ''));

    let dif, base;
    if (f1_base > f2_base) {
        dif = f1_base / f2_base;
        base = f1_base;
        f2 = f2 * dif;
    } else {
        dif = f2_base / f1_base;
        base = f2_base;
        f1 = f1 * dif;
    }

    return (f1 * f2) / base;
};

console.log(0.1 * 0.2);
console.log(toAlgebraic("0.1", "0.2"));

你可能需要重构这段代码,因为我不擅长编程:)

答案 37 :(得分:0)

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

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

答案 38 :(得分:0)

这对我有用:

function round_up( value, precision ) { 
    var pow = Math.pow ( 10, precision ); 
    return ( Math.ceil ( pow * value ) + Math.ceil ( pow * value - Math.ceil ( pow * value ) ) ) / pow; 
}

round_up(341.536, 2); // 341.54

答案 39 :(得分:0)

使用以下功能输出:

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();

注意输出toFixedCurrency(x)

答案 40 :(得分:-1)

toLocaleString方法怎么样?

> 0.1+0.2
< 0.30000000000000004

> (0.1+0.2).toLocaleString()
< 0.3

如果您希望他继续使用可计算项。

Number.parseFloat((0.1+0.2).toLocaleString())

Number.prototype.toLocaleString()

为什么不看document

> 1000.021+0.02
< 1000.0409999999999


> (1000.021+0.02).toLocaleString()
< "1,000.041"

/// why not use this?
> (1000.021+0.02).toLocaleString("zh",{useGrouping: false,maximumFractionDigits: 10})
< "1000.041"

答案 41 :(得分:-1)

优雅,可预测和可重复使用

让我们以可重复使用的优雅方式解决问题。下面的7行代码使您只需将.decimal附加到数字,公式或内置的Math函数的末尾,即可访问任意数字所需的浮点精度。

// First extend the native Number object to handle precision. This populates
// the functionality to all math operations.

Object.defineProperty(Number.prototype, "decimal", {
  get: function decimal() {
    Number.precision = "precision" in Number ? Number.precision : 3;
    var f = Math.pow(10, Number.precision);
    return Math.round( this * f ) / f;
  }
});


// Now lets see how it works by adjusting our global precision level and 
// checking our results.

console.log("'1/3 + 1/3 + 1/3 = 1' Right?");
console.log((0.3333 + 0.3333 + 0.3333).decimal == 1); // true

console.log(0.3333.decimal); // 0.333 - A raw 4 digit decimal, trimmed to 3...

Number.precision = 3;
console.log("Precision: 3");
console.log((0.8 + 0.2).decimal); // 1
console.log((0.08 + 0.02).decimal); // 0.1
console.log((0.008 + 0.002).decimal); // 0.01
console.log((0.0008 + 0.0002).decimal); // 0.001

Number.precision = 2;
console.log("Precision: 2");
console.log((0.8 + 0.2).decimal); // 1
console.log((0.08 + 0.02).decimal); // 0.1
console.log((0.008 + 0.002).decimal); // 0.01
console.log((0.0008 + 0.0002).decimal); // 0

Number.precision = 1;
console.log("Precision: 1");
console.log((0.8 + 0.2).decimal); // 1
console.log((0.08 + 0.02).decimal); // 0.1
console.log((0.008 + 0.002).decimal); // 0
console.log((0.0008 + 0.0002).decimal); // 0

Number.precision = 0;
console.log("Precision: 0");
console.log((0.8 + 0.2).decimal); // 1
console.log((0.08 + 0.02).decimal); // 0
console.log((0.008 + 0.002).decimal); // 0
console.log((0.0008 + 0.0002).decimal); // 0

干杯!

答案 42 :(得分:-2)

处理任意浮点数:

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

答案 43 :(得分:-2)

我有一个解决方法。例如,乘以10E ^ x不能与1.1一起使用。

function sum(a,b){
    var tabA = (a + "").split(".");
    var tabB = (b + "").split(".");
    decA = tabA.length>1?tabA[1].length:0;
    decB = tabB.length>1?tabB[1].length:0;
    a = (tabA[0]+tabA[1])*1.0;
    b = (tabB[0]+tabB[1])*1.0;
    var diff = decA-decB;
    if(diff >0){
        //a has more decimals than b
        b=b*Math.pow(10,diff);
        return (a+b)/Math.pow(10,decA);
    }else if (diff<0){
        //a has more decimals than b
        a=a*Math.pow(10,-diff);
                return (a+b)/Math.pow(10,decB);
    }else{
        return (a+b)/Math.pow(10,decA);
    }       
}

可怕但工作:)

答案 44 :(得分:-5)

您可以使用正则表达式来检查数字是否以长字符串0结尾,后跟一小段余数:

// using max number of 0s = 8, maximum remainder = 4 digits
x = 0.1048000000000051
parseFloat(x.toString().replace(/(\.[\d]+[1-9])0{8,}[1-9]{0,4}/, '$1'), 10)
// = 0.1048