JavaScript中添加和比较两个十进制数

4

我正在尝试将两个十进制数相加(参数可以是数字或在解析之前为数字的字符串),并将结果与resultInput进行比较。问题在于浮点数不能被系统准确地表示。例如,0.1 + 0.2 = 0.30000000000000004。因此,我尝试使用toFixed()方法以固定点表示法格式化数字。当我运行代码时,得到了false。不确定哪里出了问题。如果您有任何想法,请告诉我。

    function calc(firstNumber, secondNumber, operation, resultInput) {
      let a = parseFloat(firstNumber); //Number()
      let b = parseFloat(secondNumber); //Number()
      let c;
      let d = parseFloat(resultInput);
      console.log(JSON.stringify(`value of d : ${d}`)); //"value of d : NaN"
    
      switch (operation) {
        case '+':
          c = a + b;
          break;
        case '-':
          c = a - b;
          break;
        case '*':
          c = a * b;
          break;
        case '/':
         if (b === 0 && 1 / b === -Infinity) {
           r = Infinity;
         } else {
           r = a / b;
         }
          break;
        default:
          console.log(`Sorry, wrong operator: ${operation}.`);
      }
      console.log(JSON.stringify(`value of c: ${c}`)); // "value of c: 0.30000000000000004"
      let f = +c.toFixed(1);
      let e = +d.toFixed(1);
    
      console.log(JSON.stringify(`value of f: ${f}`)); // "value of f: 0.3"
      console.log(typeof f); //number
      console.log(JSON.stringify(`value of d: ${d}`)); // "value of d: NaN"
      console.log(typeof d); //number
      console.log(JSON.stringify(`value of e: ${e}`)); // "value of e: NaN"
      console.log(typeof e); //number
    
      if (f !== e) return false;
      // if (!Object.is(f, e)) return false;
      return true;
    }
    
    console.log(calc('0.1', '0.2', '+', '0.3'));


.toFixed() returns a string so you'll need to convert it back to a number. let f = parseFloat(c.toFixed(1)); - zer00ne
要比较两个字符串,使用 `"0.3" === "0.3" //true"。我有遗漏什么吗? - John John
不要用引号包裹数字,否则它们会变成字符串。字符串的行为与数字不同。 - zer00ne
我需要它们作为输入是字符串。这是我试图解决的任务的条件。 - John John
1
你的 result 字符串中似乎出现了一些不可见字符。你可能需要考虑解析和使用 .toFixedresult 进行精度控制,以确保准确性。 - Hamms
显示剩余4条评论
3个回答

4

与其来回转换为字符串,你可以创建一个函数来测试两个数字是否足够接近以便称之为相等。你可以决定一个很小的“delta”,如果这些数字至少接近到那个程度,那么就是可以的。

function almost(a, b, delta = 0.000001){
    return Math.abs(a - b) < delta
}

// not really equal
console.log("equal?", 0.2 + 0.1 === 0.3)

// but good enough
console.log("close enough?", almost(0.2 + 0.1, 0.3))


但是您能否解释一下为什么这段代码是错误的吗? - Conan
1
@Conan,我刚发现我发布的“0.3”中有一个特殊字符,看起来像3,但实际上不是3。解决方案是正确的。 - John John
1
@JohnJohn,这里的三是没问题的,它的字符编码是 ASCII 3。该字符串被 &#8236 和 &#8237 包围,它们是用于双向文本的不可打印控制字符。 - Mark
@MarkMeyer,这非常奇怪,因为当我在ASCII表中检查我的键盘字符时,它显示Dec-39 Hex-27 Binary-00100111 HTML-&#39; Char-' Single quote。https://www.rapidtables.com/code/text/ascii-table.html - John John

2

我运行了你的代码多次,没有任何问题。我只是发现你发布的'0.3'中有一个特殊字符,看起来像3,但实际上不是3。所以,当你想在JS上运行它时,会显示错误。所以你的解决方案是正确的。在这里查看。

最初的回答:

我已经多次运行了你的代码,并且没有发现任何问题。但是,我注意到你发布的'0.3'中包含一个特殊字符,它看起来像3,但实际上不是3。因此,在JS上运行时会出现错误。所以你的解决方案是正确的。请参考这里。

function calc(firstNumber, secondNumber, operation, resultInput) {
  let a = parseFloat(firstNumber);
  let b = parseFloat(secondNumber);
  let aux = parseFloat(resultInput);
  let r;

  switch (operation) {
    case '+':
      r = a + b;
      break;
    case '-':
      r = a - b;
      break;
    case '*':
      r = a * b;
      break;
    case '/':
      if (b !== 0) {
        r = a / b;
      } else {
        r = 0;
      }
      break;
    default:
      console.log(`Sorry, wrong operator: ${operation}.`);
  }

  return (+r.toFixed(1)) === (+aux.toFixed(1));
}

console.log(calc('0.1', '0.2', '+', '0.3'));


它返回了true,就像它应该的那样。 - John John
我仍然对这个特殊字符感到困惑。但是我会尝试去理解它。谢谢! - John John
你认为我的键盘可能被恶意软件入侵,导致我在输入数字时出现问题吗? - John John
1
嗯,我可能会这么做,以防万一备份一下。格式化你的电脑并安装一个干净的操作系统。我总是有一个干净安装的镜像。因此,只需通过恢复镜像即可在20-35分钟内获得新鲜和干净的安装。 - Teocci
我发现这个字符在数字三后面。输出(源编码:KOI-7)+ицВ-。不确定它的含义是什么。 - John John
显示剩余2条评论

1

这不是一个直接的答案,而是关于toFixed()和浮点数行为的解释:

toFixed()返回一个文本。其中一个原因是,通常不存在可以表示toFixed()结果的数字。因此,仅将此函数用于显示,而不是像这里一样用于计算。

let f = +c.toFixed(1);
let e = +d.toFixed(1);

toPrecision(18) 可以显示数字的所有相关位数。以下是一些示例:

 (0.1).toPrecision(18) // => 0.100000000000000006
 (0.2).toPrecision(18) // => 0.200000000000000011
 (0.3).toPrecision(18) // => 0.299999999999999989

这个例子解释了为什么0.1+0.2不等于0.3
同样的例子使用toFixed(1)
(+(0.1).toFixed(1)).toPrecision(18) // => 0.100000000000000006
(+(0.2).toFixed(1)).toPrecision(18) // => 0.200000000000000011
(+(0.3).toFixed(1)).toPrecision(18) // => 0.299999999999999989

它没有改变任何东西:toFixed(1)格式化了数字,+符号将其转换回最接近的现有数字。
这不是JavaScript问题,而是计算机使用IEEE 754(基于二进制的数字)的浮点数问题。大多数硬件支持此类浮点数。
在计算机数学中,通常使用绝对或相对增量值来比较浮点数的相等性。例如,使用绝对增量:
function isEqual( num1, num2, epsilon )
{
    return Math.abs( num1 - num2 ) <= epsilon;
}

isEqual( 0.1 + 0.2, 0.3, 1e-10 ) // => true

数据库支持像 DECIMAL(5.1) 这样的数据类型。这里 0.1+0.2 == 0.3,因为它们在内部使用整数并仅格式化输出。
JavaScript 示例,用于带有 2 个小数位数的货币(欧元,分):
// scan user input:
cent = round( euro * 100 );

// adding cents:
cent3 = cent1 + cent2;

// print cents as euro
(cent/100).toFixed(2)+"€"

他没有使用 toFixed() 来计算任何东西,他是将操作结果与期望结果进行比较。因此,通过添加多个转换来比较两个元素,你正在使解决方案变得更加复杂。问题很简单,我有一个数字,我需要将其与另一个可以是数字或字符串的元素进行比较。 - Teocci

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接