在 Javascript 中处理浮点精度 [duplicate]

Dealing with float precision in Javascript [duplicate]

提问人:Jeroen Ooms 提问时间:7/28/2012 最后编辑:Eric LeschinskiJeroen Ooms 更新时间:9/7/2023 访问量:284450

问:

我在javascript中有大量的数值。我想通过将它们向下舍入到最接近的倍数来对它们进行分组,并将结果转换为字符串。yx

如何解决烦人的浮点精度问题?

例如:

0.2 + 0.4 = 0.6000000000000001

我尝试过两件事:

>>> y = 1.23456789 
>>> x = 0.2 
>>> parseInt(Math.round(Math.floor(y/x))) * x; 
1.2000000000000002

和:

>>> y = 1.23456789 
>>> x = 0.2 
>>> y - (y % x)
1.2000000000000002
JavaScript 浮点 双精度 数值方法

评论

0赞 Vatev 7/28/2012
这实际上是正常行为,只是您在大多数语言的打印语句中看不到它。你试过四舍五入你的数字吗?double
3赞 Pointy 7/28/2012
你无法真正“绕过”它,因为它是二进制浮点数学系统的内在方面。显然,你的“x”和“y”值都是如此;如果“x”是 0.3,则无法准确表示。“四舍五入”到任意分数将导致不精确。
0赞 Jeroen Ooms 7/28/2012
那么,转换为 .y"1.2"
0赞 pilau 8/5/2014
@Jeroen我相信你已经得到了它,但只是为了记录,.Math.floor(y)
2赞 Josh1billion 3/1/2016
@pilau这将导致 1,而不是 1.2

答:

181赞 Rusty Fausak 7/28/2012 #1

来自这篇文章: 如何处理 JavaScript 中的浮点数精度?

您有以下几种选择:

  • 对小数使用特殊数据类型,如 decimal.js
  • 将结果格式化为固定数量的有效数字,如下所示:

(Math.floor(y/x) * x).toFixed(2)

  • 将所有数字转换为整数

评论

15赞 Karl 12/23/2014
“将所有数字转换为整数”,我一直想知道这一点。众所周知,JavaScript 有一种数字类型,即 IEEE 754 浮点数。如果是这样的话,那么为什么将浮点数转换为整数是有效的,(而且确实如此)?JavaScript 是否真的有一个根本无法通过保留字访问的整数数据类型?Number
7赞 rich remer 12/8/2015
IEEE 754 可以精确地表示高达 2^50 的整数。因此,如果您在已知范围内工作,则可以缩放值以利用 50 位(或其他)精度,而不是浪费通常为大数字保留的精度。
1赞 paul23 12/8/2017
@richremer浮点具有精度(对于正常值)不依赖于大小的属性。- 因此,无论您将其转换为整数(例如,将值乘以某个常数),精度都是相等的。
0赞 Rivenfall 7/15/2020
“有效位数”是使用科学记数法(无前导零或尾随零)的位数,它是根据情况做出的选择。- toPrecision 的参数似乎是一些有效数字。- toFixed 表示多个尾随数字。
3赞 jefelewis 12/4/2020
toFixed() 会将数字转换为字符串。
1赞 whaatt 7/28/2012 #2

在完成这项任务时,我首先会找到 中的小数位数,然后相应地四舍五入。我会使用:xy

y.toFixed(x.toString().split(".")[1].length);

它应该转换为字符串,将其拆分到小数点上,找到右侧部分的长度,然后应根据该长度四舍五入。xy.toFixed(length)y

评论

4赞 Jeroen Ooms 7/28/2012
这是一个整数,这有点问题。x
0赞 user56reinstatemonica8 11/21/2022
适用于浮点数和整数的现代 (>ES2020) 版本:Number(x.toFixed(String(y).split('.')[1]?.length ?? 0)
6赞 philipvr 7/28/2012 #3

你可以做这样的事情

> +(Math.floor(y/x)*x).toFixed(15);
1.2

编辑:最好使用big.js。

big.js

一个小巧、快速、易于使用的库,用于任意精度的十进制算术。

>> bigX = new Big(x)
>> bigY = new Big(y)
>> bigY.div(bigX).round().times(bigX).toNumber() // => 1.2

评论

0赞 Jeroen Ooms 7/28/2012
不适用于所有组合和 .yx
9赞 dopatraman 8/30/2018
toFixed 返回一个字符串
0赞 Rivenfall 7/15/2020
如果可以的话,将 floor 替换为 round、parseFloat 和 15 替换为较小的数字(toFixed 参数可以根据 x 计算)+
0赞 fider 1/19/2023
还要检查十进制 .js 或 dinero.js 中的 $
2赞 prahaladp 4/9/2013 #4

看看这个链接..这对我帮助很大。

http://www.w3schools.com/jsref/jsref_toprecision.asp

该函数返回一个,因此不要忘记使用该函数转换为所需精度的小数点。toPrecision(no_of_digits_required)stringparseFloat()

评论

1赞 prahaladp 3/7/2018
如果你碰巧是对我这个答案投反对票的人,你能解释一下为什么吗?(提供的解决方案似乎有效)
0赞 Rivenfall 7/16/2020
基本上问题是这样的,你有 js 数字 y 和 x,你想要最接近的 x 倍数(从 y)。toPrecision 方法无济于事,即parseFloat((150).toPrecision(1)) === 200
0赞 Tchakabam 1/25/2022
对舍入问题进行字符串/浮点解析转换并不完全是我所说的大规模执行:D(更好:实现/使用仅对数字进行操作/返回的 toPrecision 函数!
5赞 Hamza Alayed 3/19/2014 #5
> 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

You can check the full explanation here.

评论

0赞 joe cool 4/25/2019
Math.m(0.07, 100) => 7.000000000000002
0赞 shaiis.com 12/30/2020
This is not working!