在 Scala 中具有自定义比较的 Double 类型

Double type with custom comparation in Scala

提问人:xandor19 提问时间:9/28/2023 最后编辑:xandor19 更新时间:9/29/2023 访问量:49

问:

我正在将一些旧的 Java 代码迁移到 Scala,它使用“精度”阈值来比较实际值。已经翻译成 Scala 代码的片段将是:

object CompareDouble {
  private val EPSILON = 0.00001

  def equals(a: Double, b: Double): Boolean = a == b || Math.abs(a - b) < EPSILON

  def greater(a: Double, b: Double): Boolean = a - b > EPSILON
  .
  .
  .
}

这段代码的问题在于它的用法很容易变得冗长,例如:

  //In an interval overlap comparison
        CompareDouble.lessEquals(low1, low2) && CompareDouble.lessEquals(low2, up1) ||
          CompareDouble.lessEquals(low1, up2) && CompareDouble.lessEquals(up2, up1) ||
          CompareDouble.lessEquals(low2, low1) && CompareDouble.lessEquals(low1, up2) ||
          CompareDouble.lessEquals(low2, up1) && CompareDouble.lessEquals(up1, up2)

因此,我试图为值定义某种包装类,以允许我使用标准运算符。这个想法是能够做到:

  //Just an example of the idea
  if a == b then...
  else if a > b then...

原件在哪里等等a == bdef equals(a: Double, b: Double):

到目前为止,我已经这样做了:

class PrecisionDouble(var wrapp: Double) {
  // PrecissionDouble.EPSILON refers to a field in this class' companion object
  def == (that: PrecisionDouble): Boolean = wrapp == that.wrapp || Math.abs(wrapp - that.wrapp) < PrecisionDouble.EPSILON

  def < (that: PrecisionDouble): Boolean = (that.wrapp - wrapp) > PrecisionDouble.EPSILON

这适用于比较用例,但我必须在类中手动包装和解包该值以用于其他用法(例如数学函数或基本 aritmethics)。我想实现这个目标:

  val a = PrecisionDouble(3.2)
  val b: Double = math.sqrt(a)

并且还能够将 PrecisionDouble 比较方法中的参数类型从 替换为 ,以便能够更不详细地进行比较。def == (that: PrecisionDouble)def == (that: Double)

这在某种程度上可能吗?

编辑:Scala 3.2

Scala 包装器

评论

0赞 Gaël J 9/28/2023
Scala 2 还是 Scala 3?
0赞 Gaël J 9/28/2023
请注意,这是自己实现的有趣事情,但您可以借此机会不首先使用,而是使用固定精度。DoubleBigDecimal
0赞 xandor19 9/28/2023
检查编辑。BigDecimal 不会使用更多内存吗?

答:

4赞 Dima 9/28/2023 #1

最简单的方法可能是使用一组不同的运算符:

object Precision { 
   implicit class D(val d: Double) extends AnyVal { 
     def === (other: Double) = CompareDouble.equals(d, other)
     def >> (other: Double) = CompareDouble.grater(d, other)
     // etc.
   }
}

import Precision._

println (1.41 === math.sqrt(2))

或者,您可以在 和 之间定义隐式转换,但我认为,它是较差的,因为如果运算符相同,则在大多数情况下,转换无论如何都不会自动发生。DoublePrecisionDouble

评论

0赞 Tim 9/28/2023
定义新运算符是个好主意,但 OP 现在表示这是 Scala 3,所以应该是而不是 .extensionimplicit class
0赞 Dima 9/28/2023
这在 scala 3 中按原样工作正常。就个人而言,我不喜欢仅仅为了玩语法而玩弄版本之间的语言语法......因此,我将实时将其转换为更高级的风格,该风格在早期版本中无法编译,作为读者的练习:D
1赞 Tim 9/29/2023 #2

以下是在 Scala 3 中添加新的比较运算符的方法:

extension (d: Double)
    def === (other: Double) = CompareDouble.equals(d, other)
    def >> (other: Double) = CompareDouble.greater(d, other)

如果需要在 Scala 2 项目中使用它,则可以使用 in。CrossVersion.for2_13Use3sbt