理解浮点问题

IT技术 c# javascript vb.net floating-point
2021-02-08 10:18:12

这里有人可以帮助我了解如何确定浮点限制何时会导致计算错误。例如下面的代码。

CalculateTotalTax = function (TaxRate, TaxFreePrice) {
     return ((parseFloat(TaxFreePrice) / 100) * parseFloat(TaxRate)).toFixed(4);
};

我一直无法输入任何两个导致此方法结果不正确的值。如果我删除 toFixed(4) 我实际上可以看到计算开始失去准确性的地方(小数点后第 6 位左右)。尽管如此,我对浮点数的理解是,即使是很小的数字有时也无法表示或被我误解,并且可以始终准确表示 4 个小数位(例如)。

MSDN解释花车为这样的...

这意味着它们不能准确表示任何不是二进制分数的数量(形式为 k / (2 ^ n),其中 k 和 n 是整数)

现在我假设这适用于所有浮点数(包括 javascript 中使用的浮点数)。

从根本上说,我的问题归结为这个。如何确定任何特定方法是否容易受到浮点运算中的错误的影响,这些错误将以什么精度出现以及产生这些错误需要哪些输入?

希望我问的是有道理的。

4个回答

首先阅读每个计算机科学家应该知道的关于浮点的知识http : //docs.sun.com/source/806-3568/ncg_goldberg.html

简短回答:双精度浮点数(这是 JavaScript 中的默认值)具有大约 16 位十进制数字的精度。四舍五入可能因平台而异。如果您得到始终如一的正确答案是绝对必要的,那么您应该自己进行有理算术(这不需要很难 - 对于货币,也许您可​​以乘以 100 以将美分数存储为整数)。

但是如果能得到高精度的答案,浮点数就足够了,尤其是双精度。

在处理浮动时,您现在应该做两件重要的事情:

1- 您应该了解machine epsilon要知道你有多少精度。

2- 您不应该假设两个值是否以 10 为基数相等,则它们在具有精度限制的机器中以 2 为基数相等

if ((6.0 / 10.0) / 3.0 != .2) {
        cout << "gotcha" << endl;
}

数字 2 可能足以使您避免比较浮点数的相等性,而是可以使用阈值和大于或小于运算符进行比较

谢谢@Shedal。更正了答案。
2021-03-22 10:18:12
正如您所说,应该花时间学习浮点数;像 (0.1 == 1/10) 这样的东西看起来很无辜,除非他之前读过它,否则没有人可能会认为它会导致错误。即使谈论数学基础知识,也没有多少人知道 10 = 9.999.... 或者如何将十进制转换为二进制。
2021-03-31 10:18:12
原因.1 != 1/10是因为1/10 == 0您正在使用整数除法。
2021-04-03 10:18:12
数字 2 并不意味着你“不应该比较浮点数的相等性”;毕竟,您永远不应该比较十进制数字的相等性,这同样很容易。这意味着人们不应该编写自己不理解的代码。花点时间了解浮点数的工作原理。
2021-04-10 10:18:12

其他答案指出了很好的资源来理解这个问题。如果您在代码中实际使用货币值(如您的示例中所示),您应该更喜欢 Decimal 类型(.Net 中的 System.Decimal)。这些将避免使用浮点数时的一些舍入问题并更好地匹配域。

不,小数位数与可以表示的内容无关。

尝试 .1 * 3,或 162.295 / 10,或 24.0 + 47.98。那些在 JS 中对我来说失败了。但是,24.0 * 47.98 不会失败。

因此,要回答您的三个问题,任何精度的任何操作都可能容易受到攻击。给定的输入是否会是一个我不知道如何回答的问题,但我有一种预感,有很多因素。1) 实际答案与最接近的二进制分数的接近程度。2) 执行计算的引擎的精度。3) 用于执行计算的方法(例如,乘以位移可能给出与乘以重复加法不同的结果)