开发者

(Number, Number) matches (Float, Int) but does not match (Int, Float)

Is it a bug in Scala 2.8.0 ? (the same happens with 2.8.1.RC2)

import junit.framework._
import Assert._

class BugTest extends TestCase {

  def compare(first: Any, second: Any): Int = {
      (first, second) match {
        case (k: Int, o: Int) => k compare o
        //why the next case matches (Float, Int) but does not match (Int, F开发者_如何学运维loat) ???
        case (k: Number, o: Number) => k.doubleValue() compare o.doubleValue()
        case _ => throw new Exception("Unsupported compare " + first + "; " + second)
    }
  }

  def testCompare() {
    assertEquals("Both Int", -1, compare(0, 1))
    assertEquals("Both Float", 1, compare(1.0, 0.0))
    assertEquals("Float then Int", 0, compare(10.0, 10))
    assertEquals("Int then Float", 0, compare(10, 10.0))//this fails with an exception
  }
}


I think this is a bug. If you look at the generated bytecode:


public int compare(java.lang.Object, java.lang.Object);
  Code:
   Stack=4, Locals=7, Args_size=3
   0:   aload_1
   1:   astore_3
   2:   aload_2
   3:   astore  4
   5:   aload_3
   6:   instanceof  #8; //class java/lang/Integer
   9:   ifeq    81
   12:  aload_3
   13:  invokestatic    #14; //Method scala/runtime/BoxesRunTime.unboxToInt:(Ljava/lang/Object;)I
   16:  istore  5
   18:  aload   4
   20:  instanceof  #8; //class java/lang/Integer
   23:  ifeq    45
   26:  getstatic   #20; //Field scala/Predef$.MODULE$:Lscala/Predef$;
   29:  iload   5
   31:  invokevirtual   #24; //Method scala/Predef$.intWrapper:(I)Lscala/runtime/RichInt;
   34:  aload   4
   36:  invokestatic    #14; //Method scala/runtime/BoxesRunTime.unboxToInt:(Ljava/lang/Object;)I
   39:  invokevirtual   #29; //Method scala/runtime/RichInt.compare:(I)I
   42:  goto    124
   45:  new #31; //class java/lang/Exception

   //part omitted for brevity..

   81:  aload_3
   82:  instanceof  #54; //class java/lang/Number
   85:  ifeq    161
   88:  aload_3
   89:  checkcast   #54; //class java/lang/Number
   92:  astore  6
   94:  aload   4
   96:  instanceof  #54; //class java/lang/Number
   99:  ifeq    125
   102: getstatic   #20; //Field scala/Predef$.MODULE$:Lscala/Predef$;
   105: aload   6
   107: invokevirtual   #58; //Method java/lang/Number.doubleValue:()D
   110: invokevirtual   #62; //Method scala/Predef$.doubleWrapper:(D)Lscala/runtime/RichDouble;
   113: aload   4
   115: checkcast   #54; //class java/lang/Number
   118: invokevirtual   #58; //Method java/lang/Number.doubleValue:()D

In line 6, the check is done whether the first variable is an instance of java.lang.Integer. If this does not succeed, we continue at line 81, which starts with the java.lang.Number check. If the first variable is an Integer, then we continue with the same check for the second variable. However, if this second check does not succeed, instead of again continuing at line 81 with the Number check, a jump is made to line 45, which throws the Exception. That does not seem to be correct. I quickly browsed through trac but couldn't directly find an issue regarding this, so it may be sensible to create one.

Edit As Extempore pointed out, it is a bug that is already in trac, see his comment below.


I don't know the answer to the specific question, but here's a different way to define compare:

def compare[A : Numeric, B: Numeric](first: A, second: B): Int =
  implicitly[Numeric[A]].toDouble(first) compare implicitly[Numeric[B]].toDouble(second)

scala> compare(0.0,1.0)
res30: Int = -1

scala> compare(0.0,1)
res31: Int = -1

scala> compare(0,1.0)
res32: Int = -1

scala> compare(0,1)
res33: Int = -1
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜