Kotlin 中 Double 和 Int 数字的错误比较
Wrong comparing of Double and Int numbers in Kotlin
我正在从事计算器项目,但是当我尝试使用两种类似的方法来比较双精度数和整数时,我得到了不同的结果。
所以我的问题是为什么这些比较方式不同?
//some code that parse the string
//...
//code of calculating:
fun calculateIn(queueNumbers: Queue<Double>, queueActions: Queue<Char>) {
var action: Char
var result = queueNumbers.poll()
var operand: Double
while (!queueNumbers.isEmpty()) {
operand = queueNumbers.poll()
action = queueActions.poll()
when (action) {
'-' -> result -= operand
'+' -> result += operand
'*' -> result *= operand
'/' -> result /= operand
'%' -> result = result % operand * -1.0
}
}
var pointNum = 8.3
println("pointNum = " + pointNum)
println(if(pointNum.compareTo(pointNum.toInt()) == 0) pointNum.toInt() else pointNum)
println("result = " + result)
println(if(result.compareTo(result.toInt()) == 0) result.toInt() else result)
}
代码结果:
"10.3 + -2" //input String
[10.3, -2.0] //queueNumbers
[+]//queueActions
pointNum = 8.3
8.3
result = 8.3
8
我觉得这很奇怪,因为如果我 运行 类似的代码,我会得到正确的结果:
var pointNum = 8.3
println(if(pointNum.compareTo(pointNum.toInt()) == 0) pointNum.toInt() else pointNum)
所以这段代码的结果是:
8.3
GitHub 上的完整代码:https://github.com/Trilgon/LearningKotlin
这对我来说是一个奇怪甚至有趣的问题,但我想我发现了问题所在。
在 Kotlin 中 Double
class 是在 Primitives.kt
中定义的,至少在 build-in files not in the actual source. Here Double
class implements an interface named Comparable<Double>
which is another built-in Kotlin 文件中是这样。您可以看到该界面的一部分:
public operator fun compareTo(other: T): Int
所以让我们 return 到您的 code。我暂停这个问题的罪魁祸首是第一行。
import java.util.*
在 java.util
包中还有另一个 Comparable
接口,我想因为 Queue
是 Java class 而 poll
方法可能 return 一个可为 null 的值,并且由于您已经将所有内容导入 java.util
包中,因此 compareTo
从 Comparable
从 java.util
调用而不是 Kotlin 包中的那个.
我不知道 Java compareTo
return 不同结果的原因。
我尝试将 java.util.*
替换为:
import java.util.Queue
import java.util.LinkedList
但没有任何改变。
不过,我找到了另一种解决方案,只需replacevar result = queueNumbers.poll()
加上var result = queueNumbers.poll() ?: 0.0
或var result: Double = queueNumbers.poll()
就可以解决了!
//original
var result = queueNumbers.poll()
//method 1
var result : Double = queueNumbers.poll()
//method 2
var result = queueNumbers.poll() ?: 0.0
我认为这是编译器中的错误。讨论是here,我复制我的发现:
最小可重现示例:
fun test1(): Int {
val d: Double?
d = 8.3
return d.compareTo(8) // 0
}
fun test2(): Int {
val d: Double
d = 8.3
return d.compareTo(8) // 1
}
这两个代码示例之间的技术差异在于值在 test1()
中装箱,在 test2()
中取消装箱。如果我们查看为 test2()
生成的字节码,一切看起来都符合预期:
7: bipush 8
9: i2d
10: invokestatic #63 // Method java/lang/Double.compare:(DD)I
它将整数值 8
转换为双精度值,然后将两个值作为双精度值进行比较。
但是如果我们查看 test1()
,那里会发生一些奇怪的事情:
10: invokevirtual #52 // Method java/lang/Double.doubleValue:()D
13: d2i
14: bipush 8
16: invokestatic #58 // Method kotlin/jvm/internal/Intrinsics.compare:(II)I
它做相反的事情:它将双精度值 8.3
转换为整数,然后将值作为整数进行比较。这就是为什么它说值是相同的。
有趣的是,即使是 IntelliJ 中的“Kotlin Bytecode”工具也能显示 test1()
:
的正确代码
INVOKEVIRTUAL java/lang/Double.doubleValue ()D
BIPUSH 8
I2D
INVOKESTATIC java/lang/Double.compare (DD)I
但真正生成的字节码是不同的,给出不同的结果。
我正在从事计算器项目,但是当我尝试使用两种类似的方法来比较双精度数和整数时,我得到了不同的结果。 所以我的问题是为什么这些比较方式不同?
//some code that parse the string
//...
//code of calculating:
fun calculateIn(queueNumbers: Queue<Double>, queueActions: Queue<Char>) {
var action: Char
var result = queueNumbers.poll()
var operand: Double
while (!queueNumbers.isEmpty()) {
operand = queueNumbers.poll()
action = queueActions.poll()
when (action) {
'-' -> result -= operand
'+' -> result += operand
'*' -> result *= operand
'/' -> result /= operand
'%' -> result = result % operand * -1.0
}
}
var pointNum = 8.3
println("pointNum = " + pointNum)
println(if(pointNum.compareTo(pointNum.toInt()) == 0) pointNum.toInt() else pointNum)
println("result = " + result)
println(if(result.compareTo(result.toInt()) == 0) result.toInt() else result)
}
代码结果:
"10.3 + -2" //input String
[10.3, -2.0] //queueNumbers
[+]//queueActions
pointNum = 8.3
8.3
result = 8.3
8
我觉得这很奇怪,因为如果我 运行 类似的代码,我会得到正确的结果:
var pointNum = 8.3
println(if(pointNum.compareTo(pointNum.toInt()) == 0) pointNum.toInt() else pointNum)
所以这段代码的结果是:
8.3
GitHub 上的完整代码:https://github.com/Trilgon/LearningKotlin
这对我来说是一个奇怪甚至有趣的问题,但我想我发现了问题所在。
在 Kotlin 中 Double
class 是在 Primitives.kt
中定义的,至少在 build-in files not in the actual source. Here Double
class implements an interface named Comparable<Double>
which is another built-in Kotlin 文件中是这样。您可以看到该界面的一部分:
public operator fun compareTo(other: T): Int
所以让我们 return 到您的 code。我暂停这个问题的罪魁祸首是第一行。
import java.util.*
在 java.util
包中还有另一个 Comparable
接口,我想因为 Queue
是 Java class 而 poll
方法可能 return 一个可为 null 的值,并且由于您已经将所有内容导入 java.util
包中,因此 compareTo
从 Comparable
从 java.util
调用而不是 Kotlin 包中的那个.
我不知道 Java compareTo
return 不同结果的原因。
我尝试将 java.util.*
替换为:
import java.util.Queue
import java.util.LinkedList
但没有任何改变。
不过,我找到了另一种解决方案,只需replacevar result = queueNumbers.poll()
加上var result = queueNumbers.poll() ?: 0.0
或var result: Double = queueNumbers.poll()
就可以解决了!
//original
var result = queueNumbers.poll()
//method 1
var result : Double = queueNumbers.poll()
//method 2
var result = queueNumbers.poll() ?: 0.0
我认为这是编译器中的错误。讨论是here,我复制我的发现:
最小可重现示例:
fun test1(): Int {
val d: Double?
d = 8.3
return d.compareTo(8) // 0
}
fun test2(): Int {
val d: Double
d = 8.3
return d.compareTo(8) // 1
}
这两个代码示例之间的技术差异在于值在 test1()
中装箱,在 test2()
中取消装箱。如果我们查看为 test2()
生成的字节码,一切看起来都符合预期:
7: bipush 8
9: i2d
10: invokestatic #63 // Method java/lang/Double.compare:(DD)I
它将整数值 8
转换为双精度值,然后将两个值作为双精度值进行比较。
但是如果我们查看 test1()
,那里会发生一些奇怪的事情:
10: invokevirtual #52 // Method java/lang/Double.doubleValue:()D
13: d2i
14: bipush 8
16: invokestatic #58 // Method kotlin/jvm/internal/Intrinsics.compare:(II)I
它做相反的事情:它将双精度值 8.3
转换为整数,然后将值作为整数进行比较。这就是为什么它说值是相同的。
有趣的是,即使是 IntelliJ 中的“Kotlin Bytecode”工具也能显示 test1()
:
INVOKEVIRTUAL java/lang/Double.doubleValue ()D
BIPUSH 8
I2D
INVOKESTATIC java/lang/Double.compare (DD)I
但真正生成的字节码是不同的,给出不同的结果。