在 .NET 中检查两个双精度数的相等性时出现问题——这个方法有什么问题?

发布于 2024-09-13 18:59:08 字数 1400 浏览 2 评论 0原文

所以我要深入探讨这个问题...我有一个频繁使用的 Web 应用程序,两年来第一次未能使用同事所说的相等函数对两个双精度数进行相等检查d 也已使用多年。

我要粘贴到此处的函数的目标是将两个双精度值与 4 位精度进行比较并返回比较结果。为了便于说明,我的值是:

Dim double1 As Double = 0.14625000000000002 ' The result of a calculation
Dim double2 As Double = 0.14625 ' A value that was looked up in a DB

如果我将它们传递到此函数中:

Public Shared Function AreEqual(ByVal double1 As Double, ByVal double2 As Double) As Boolean

    Return (CType(double1 * 10000, Long) = CType(double2 * 10000, Long))

End Function

比较失败。相乘并转换为 Long 后,比较结果如下:

Return 1463 = 1462

我在这里回答了我自己的问题,但我可以看到 double1 在 double(17 位数字)的精度范围内,并且演员工作正常。

我的第一个真正的问题是:如果我将上面的行更改为以下行,为什么它可以正常工作(返回 True)?

Return (CType(CType(double1, Decimal) * 10000, Long) = _
    CType(CType(double2, Decimal) * 10000, Long))

Decimal 是否具有更高的精度,因此转换为 Long 仍应为 1463,并且比较返回 False?我想我在这件事上脑子里放屁了......

其次,如果要改变这个函数以使我正在寻找更准确或更不容易出错的比较,你会建议将其更改为更简单的东西吗?例如:

Return (Math.Abs(double1 - double2) < 0.0001)

我会疯狂地尝试类似的事情吗:(

Return (double1.ToString("N5").Equals(double2.ToString("N5")))

我永远不会做上述事情,我只是对你的反应感到好奇。这在我的应用程序中效率非常低。)

无论如何,如果有人可以阐明其中的差异我发现将 Double 和 Decimal 转换为 Long 会很棒。

谢谢!

So I'm just going to dive into this issue... I've got a heavily used web application that, for the first time in 2 years, failed doing an equality check on two doubles using the equality function a colleague said he'd also been using for years.

The goal of the function I'm about to paste in here is to compare two double values to 4 digits of precision and return the comparison results. For the sake of illustration, my values are:

Dim double1 As Double = 0.14625000000000002 ' The result of a calculation
Dim double2 As Double = 0.14625 ' A value that was looked up in a DB

If I pass them into this function:

Public Shared Function AreEqual(ByVal double1 As Double, ByVal double2 As Double) As Boolean

    Return (CType(double1 * 10000, Long) = CType(double2 * 10000, Long))

End Function

the comparison fails. After the multiplication and cast to Long, the comparison ends up being:

Return 1463 = 1462

I'm kind of answering my own question here, but I can see that double1 is within the precision of a double (17 digits) and the cast is working correctly.

My first real question is: If I change the line above to the following, why does it work correctly (returns True)?

Return (CType(CType(double1, Decimal) * 10000, Long) = _
    CType(CType(double2, Decimal) * 10000, Long))

Doesn't Decimal have even more precision, thus the cast to Long should still be 1463, and the comparison return False? I think I'm having a brain fart on this stuff...

Secondly, if one were to change this function to make the comparison I'm looking for more accurate or less error prone, would you recommend changing it to something much simpler? For example:

Return (Math.Abs(double1 - double2) < 0.0001)

Would I be crazy to try something like:

Return (double1.ToString("N5").Equals(double2.ToString("N5")))

(I would never do the above, I'm just curious about your reactions. It would be horribly inefficient in my application.)

Anyway, if someone could shed some light on the difference I'm seeing between casting Doubles and Decimals to Long, that would be great.

Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

童话 2024-09-20 18:59:08

在这种情况下依赖强制转换很容易出错,正如您所发现的 - 根据强制转换时使用的规则,您可能不会得到您期望的数字。

我强烈建议您编写不带强制类型转换的比较代码。你的 Math.Abs​​ 线非常好。

关于你的第一个问题:

我的第一个真正的问题是:如果我改变
上面一行到下面一行,为什么
它工作正常吗(返回 True)?

原因是从 Double 到 Decimal 的转换丢失了精度,导致比较结果为 0.1425 到 0.1425。

Relying on a cast in this situation is error prone, as you have discovered - depending upon the rules used when casting, you may not get the number you expect.

I would strongly advise you to write the comparison code without a cast. Your Math.Abs line is perfectly fine.

Regarding your first question:

My first real question is: If I change
the line above to the following, why
does it work correctly (returns True)?

The reason is that the cast from Double to Decimal is losing precision, resulting in a comparison of 0.1425 to 0.1425.

吝吻 2024-09-20 18:59:08

当您使用CType时,您是在告诉您的程序“我不关心您如何对数字进行舍入;只需确保结果是其他类型”。这并不完全是您在比较数字时想要对程序说的内容。

比较浮点数是一件痛苦的事情,我永远不会相信任何语言中的 Round 函数,除非您确切知道它的行为方式(例如有时它会将 0.5 向上舍入)有时会下降,具体取决于之前的数字……一团糟)。

在 .NET 中,我实际上可能会在乘以双精度值后使用 Math.Truncate() 。因此,Math.Truncate(.14625 * 10000)(即Math.Truncate(1462.5))将等于1462,因为它得到去掉所有小数值。将 Truncate() 与示例中的数据一起使用,两个值最终将相等,因为 1) 它们仍然是双精度数,2) 您确保从每个值中删除了小数。

实际上,我认为在这种情况下字符串比较并不是很糟糕,因为浮点比较本身就非常糟糕。当然,如果您要比较数字,最好坚持使用数字类型,但使用字符串比较是另一种选择。

When you use CType, you're telling your program "I don't care how you round the numbers; just make sure the result is this other type". That's not exactly what you want to say to your program when comparing numbers.

Comparing floating-point numbers is a pain and I wouldn't ever trust a Round function in any language unless you know exactly how it behaves (e.g. sometimes it rounds .5 up and sometimes down, depending on the previous number...it's a mess).

In .NET, I might actually use Math.Truncate() after multiplying out my double value. So, Math.Truncate(.14625 * 10000) (which is Math.Truncate(1462.5)) is going to equal 1462 because it gets rid of all decimal values. Using Truncate() with the data from your example, both values would end up being equal because 1) they remain doubles and 2) you made sure the decimal was removed from each.

I actually don't think String comparison is very bad in this situation since floating point comparison is pretty nasty in itself. Granted, if you're comparing numbers, it's probably better to stick with numeric types, but using string comparison is another option.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文