如何处理JavaScript中的浮点数精度?
我有以下虚拟测试脚本:如何处理JavaScript中的浮点数精度?,javascript,floating-point,Javascript,Floating Point,我有以下虚拟测试脚本: 功能测试(){ var x=0.1*0.2; 文件。编写(x); } test()看一看。如果您想要操作的数字范围很小(例如,货币),那么它可能会解决您的问题。我会将它四舍五入到几个十进制值,这是最简单的解决方案 使用 var x = 0.1*0.2; x =Math.round(x*Math.pow(10,2))/Math.pow(10,2); 试试我的智利算术库,你可以看到。 如果您想要一个更高版本,我可以给您一个。您不能用二进制浮点类型(ECMAScript使
功能测试(){
var x=0.1*0.2;
文件。编写(x);
}
test()代码>看一看。如果您想要操作的数字范围很小(例如,货币),那么它可能会解决您的问题。我会将它四舍五入到几个十进制值,这是最简单的解决方案 使用
var x = 0.1*0.2;
x =Math.round(x*Math.pow(10,2))/Math.pow(10,2);
试试我的智利算术库,你可以看到。
如果您想要一个更高版本,我可以给您一个。您不能用二进制浮点类型(ECMAScript使用二进制浮点类型来表示浮点值)精确地表示大多数小数。因此,除非您使用任意精度的算术类型或基于十进制的浮点类型,否则没有一个优雅的解决方案。例如,.你只需要决定你到底想要多少位小数-不能既吃蛋糕又吃蛋糕:-)
随着每一次进一步的操作,数值误差都会不断累积,如果你不尽早切断它,它只会越来越大。数字库显示的结果看起来很干净,只需在每一步切断最后2位数字,数字协处理器也有“正常”和“完整”长度,原因相同。Cuf-offs对于处理器来说很便宜,但是对于脚本来说非常昂贵(乘法、除法和使用pov(…)。好的数学库会为你提供一个截断点(x,n)
因此,至少您应该使用pov(10,n)使全局var/恒定-这意味着您决定了所需的精度:-),然后执行以下操作:
你也可以继续做数学运算,最后只截取数据——假设你只显示结果,而不做if-s。如果您能做到这一点,那么.toFixed(…)可能会更有效
如果您正在进行If-s/比较,并且不想减少,那么您还需要一个小常量,通常称为eps,它比最大预期误差高一个小数位。假设您的截止值是最后两位小数-那么您的eps在最后一位的第三位有1(第三位最不显著),您可以使用它来比较结果是否在预期的eps范围内(0.02-eps<0.1*0.2<0.02+eps)。您正在寻找一个用于JavaScript的sprintf
实现,这样您就可以以您期望的格式写出带有小错误的浮点(因为它们是以二进制格式存储的)
试试看,你会这样称呼它:
var yourString = sprintf("%.2f", yourNumber);
将数字打印为小数点后两位的浮点数
如果您不想包含更多的文件,而只是为了将浮点舍入到给定的精度,那么也可以将其用于显示目的 你说得对,原因是浮点数的精度有限。将有理数存储为两个整数的除法,在大多数情况下,您将能够在不损失任何精度的情况下存储数字。在打印时,您可能希望将结果显示为分数。有了我提出的表述,它就变得微不足道了
当然,这对非理性数字没有多大帮助。但是,您可能希望优化计算,使其产生的问题最少(例如,检测类似sqrt(3)^2)
---或---
---也---
---如---
此函数将从两个浮点数的乘法中确定所需的精度,并返回具有适当精度的结果。虽然不是很优雅
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);
}
您得到的结果是正确的,并且在不同语言、处理器和操作系统中的浮点实现之间相当一致——唯一改变的是当浮点实际上是双精度(或更高)时的不准确程度
二进制浮点中的0.1就像十进制中的1/3(即0.3333…永远),只是没有精确的方法来处理它
如果您处理的是浮动,那么您总是希望有小的舍入误差,因此您也总是必须将显示的结果舍入到合理的范围内。作为回报,您可以得到非常快速和强大的算法,因为所有的计算都是在处理器的本地二进制中进行的
大多数情况下,解决方案不是切换到定点算法,主要是因为它的速度要慢得多,99%的情况下,你不需要精确性。如果你处理的东西确实需要这样的准确度(例如金融交易),Javascript可能不是最好的工具(因为你想强制使用定点类型,静态语言可能更好)
您正在寻找优雅的解决方案,但恐怕就是这样:浮点运算很快,但舍入误差很小-在显示结果时始终舍入到合理的值。要避免这种情况,您应该使用整数值而不是浮点。所以,当你想要2个位置的精度值为*100时,对于3个位置,使用1000。显示时,使用格式化程序放入分隔符
许多系统以这种方式忽略了使用小数。这就是为什么许多系统使用美分(作为整数)而不是美元/欧元(作为浮点)的原因。来自:
如何避免此问题?
这取决于什么样的环境
你正在做的计算
- 如果你真的需要把结果加起来,特别是当你
与钱打交道:使用特殊的小数点
数据类型
- 如果你只是不想看到那些额外的小数位:简单地说
将结果四舍五入为固定格式
输入时的小数位数
展示它
- 如果没有可用的十进制数据类型,另一种方法是工作
使用整数,例如domoney
计算完全以美分为单位。但是
这是更多的工作,并有一些
缺点
请注意,第一点仅适用于确实需要特定精确小数行为的情况。大多数人
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
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);
}
function strip(number) {
return (parseFloat(number).toPrecision(12));
}
num = .01 + .06; // yields 0.0699999999999
rnum = round(num,12); // yields 0.07
> 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
function test(){
var x = 0.1 * 0.2;
document.write(Number(x).toFixed(2));
}
test();
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);
function dec( num )
{
var p = 100;
return Math.round( num * p ) / p;
}
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;
0.1 + 0.2 === 0.3 // which returns false
function epsEqu(x, y) {
return Math.abs(x - y) < Number.EPSILON;
}
console.log(epsEqu(0.1+0.2, 0.3)); // true
function precisionRound(number, precision) {
var factor = Math.pow(10, precision);
return Math.round(number * factor) / factor;
}
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
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}`;
}
(0.1).toPrecision(100) ->
0.1000000000000000055511151231257827021181583404541015625000000000000000000000000000000000000000000000
(0.1+0.2).toPrecision(100) ->
0.3000000000000000444089209850062616169452667236328125000000000000000000000000000000000000000000000000
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;
}