3

我的问题的简短版本是:

x什么被认为是“最佳实践”来决定浮点数何时Math.round(x)可以被认为是相等的,从而允许浮点运算的精度损失?


冗长的版本是:

我经常需要决定是否x应该将给定的浮点值“视为整数”,或者更迂腐地说,是否应“视为整数的浮点表示”。

(例如,如果n是整数,则数学表达式

日志10 (10 n )

是表示相同整数n的复杂方式。这就是促使说类似浮点计算的结果可以被视为“整数的表示”的想法。)

任何时候做决定都很容易Math.round(x) == x每当评估为true:在这种情况下,我们可以说它x确实是整数(的浮点表示)。

但是测试Math.round(x) == x当它评估为 时,测试是不确定的false。例如,

function log10(x) { return Math.log(x)/Math.LN10; }
// -> function()
x = log10(Math.pow(10, -4))
// -> -3.999999999999999
Math.round(x) == x
// -> false

编辑:我经常看到的一个“解决方案”是选择一些任意的容差,比如ε = 1e-6,并测试Math.abs(Math.round(x) - x) < ε. 我认为这样的解决方案会产生比我认为可接受的更多的误报。

4

3 回答 3

5

正如您在示例x中看到的那样,实际上根本不是整数。这是由于您的计算中较早出现舍入错误,因此您实际上无法知道x被定义为近似舍入数还是因舍入错误而出现锯齿状的舍入数。

如果您想知道哪个数字是一个或另一个,您将需要使用您自己建议的极限方法,或者使用足够高的精度以使您的数字不会在一开始就出现锯齿状。最后一种方法并非适用于所有情况。

还可以象征性地跟踪所有数学运算,即存储1/31/3而不是0.3333按需评估它们,以消除可以像手动评估表达式时那样取消的因素,但这在几乎所有情况下都是多余的。更不用说这样的系统会有多复杂。如果这是所需的解决方案,您可能可以与 MatLab 或 Mathematica 或其他处理评估的东西进行交互,除非您在浏览器中运行它,因为浏览器可能更难尝试 WolframAlpha API(我为什么不认为第一次?)。

尽管如此; 如果您可以通过选择ε这样的方式来解决此问题,那么您将获得令人满意的结果,这可能是最好的方法。如果静态ε不削减它,您可以尝试根据手头数字之前完成的计算类型动态选择它。即相乘的数字往往比相除的数字产生更少的分数部分,依此类推。如果数字除了加、减和乘法(不涉及分数)之外没有其他任何东西,您也可以知道它最大可以有多少个小数位,因此可以选择一个合理的ε.

于 2013-02-03T01:50:01.970 回答
1

这很有趣,所以我不得不考虑一下。

这是一个单行解决方案,虽然它涉及数字和字符串类型之间的转换,所以我不知道它有多优化。但这比仅仅选择一个最小阈值并检查该数字是否在该限制范围内要准确得多。

JavaScript 数字是双精度 64 位格式,具有大约 16 位十进制数字的精度。这是总位数,而不仅仅是小数点右侧的位数。

JavaScript 数字还有一个 toPrecision() 方法,可以将它们转换为字符串,四舍五入到给定的精度(总位数,非常适合您使用)。以下将任何数字四舍五入到最接近的 15 位精度,然后将其转换回浮点数。

function roundToPrecision(number, precision) {
    return parseFloat(number.toPrecision(precision));
}

x = roundToPrecision(x, 15);

那么您的示例实际上将是一个整数:-4。

编辑:经过更多思考,这会更快:

var integerDigits = (""+parseInt(Math.abs(x))).length,
    threshold = 1e-16 * Math.pow(10, integerDigits);

Math.abs(Math.round(x) - x) < threshold

http://jsperf.com/number-precision-rounding

于 2013-02-03T06:36:39.717 回答
1

假设x非零,我认为您应该查看 ratio Math.abs(x-Math.round(x))/x。这涉及浮点类型每个存储固定数量的有效位,而不是小数点后固定数量的数字的事实。

接下来,您需要确定计算的典型舍入误差。如果x是简单计算的结果,可能很容易。如果没有,请考虑从您知道确切答案的测试用例中收集一些统计数据。x理想情况下,您会发现整数比率的最大值与该比率的最小值之间存在显着差异x,不应将其视为整数。如果是这样,请在该范围内选择一个 epsilon。

于 2013-02-03T07:23:54.323 回答