如何在Scala中比较浮点数值?

26
据我所知,使用浮点数进行精确比较没有多大意义,因为本来应该是0.0001的值实际上可能会变成像0.0001000...0001这样。我应该实现自己的比较函数来指定精度,还是有一种常见的做法可以解决这个问题?
我曾经在C#中使用以下代码(我怀疑这仍然是错误的,因为Double值可能根本无法精确表示0.0001,即使将其设置为常量(如Michael Borgwardt在这里中所解释的那样)):
public static bool AlmostEquals(this double x, double y, double precision = 0.0001)
{
  if (precision < 0.0)
    throw new ArgumentException();

  return Math.Abs(x - y) <= precision;
}

我应该在Scala中做类似的事情吗?

3个回答

30

是的,你可以像在Java中那样做。您还可以使用Scala的一些很酷的功能,通过一个~=方法将Double类进行改进,该方法需要一个隐式精度参数,只需要指定一次。

scala> case class Precision(val p:Double)
defined class Precision

scala> class withAlmostEquals(d:Double) {
  def ~=(d2:Double)(implicit p:Precision) = (d-d2).abs <= p.p
}
defined class withAlmostEquals

scala> implicit def add_~=(d:Double) = new withAlmostEquals(d)
add_$tilde$eq: (d: Double)withAlmostEquals

scala> 0.0~=0.0
<console>:12: error: could not find implicit value for parameter p: Precision
              0.0~=0.0
                 ^

scala> implicit val precision = Precision(0.001)
precision: Precision = Precision(0.001)

scala> 0.0 ~= 0.00001
res1: Boolean = true

应该记住,如果在使用C或C++的环境中,这可能会(我认为会)与按位~运算符引起混淆。 - Larry OBrien
1
(从Scala控制台返回几分钟后...)可以使用def =~=(d2:Double)(implicit p:Precision)~== - Larry OBrien

12

使用scalautils中的Tolerance

import org.scalautils._
import TripleEquals._
import Tolerance._

val result = 2.000001

结果:Double = 2.000001

result === 2.0 +- .001

res0: 布尔值 = 真

result === 2.0 +- .000000001

res1:布尔值 = false

更新:适用于Scala 2.11

import org.scalatest._
import org.scalatest.Matchers._
val r = 4
val rr = (r === 2 +- 1)

r: Int = 4 
rr: Boolean = false

1
这对于相等性很好用,但是scalautils是否也提供了一些关于epsilon比较的东西,例如a <== b作为a + 0.00001 < b? - Suma
另一种 Scala 2.1 的方式 - Roman Zykov

11

或使用2.10版本的...

case class Precision(p:Double)

implicit class DoubleWithAlmostEquals(val d:Double) extends AnyVal {
  def ~=(d2:Double)(implicit p:Precision) = (d - d2).abs < p.p
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接