数字类型
不同类型的数字以不同的方式存储。
如果你创建一个标识符并给它赋一个整数值,Kotlin 会推断它的类型为 Int
:
// NumberTypes/InferInt.kt
fun main() {
val million = 1_000_000 // 推断为 Int
println(million)
}
/* 输出:
1000000
*/
为了提高可读性,在数字值中,Kotlin 允许在数字之间使用下划线。
基本的数学运算符与大多数编程语言中可用的一样:加法(+
)、减法(-
)、除法(/
)、乘法(*
)和模运算(%
),它会产生整数除法的余数:
// NumberTypes/Modulus.kt
fun main() {
val numerator: Int = 19
val denominator: Int = 10
println(numerator % denominator)
}
/* 输出:
9
*/
整数除法会截断其结果:
// NumberTypes/IntDivisionTruncates.kt
fun main() {
val numerator: Int = 19
val denominator: Int = 10
println(numerator / denominator)
}
/* 输出:
1
*/
如果运算会对结果四舍五入,输出会是 2
。
运算的优先级遵循基本算术规则:
// NumberTypes/OpOrder.kt
fun main() {
println(45 + 5 * 6)
}
/* 输出:
75
*/
乘法操作 5 * 6
首先进行,然后是加法 45 + 30
。
如果你想先进行 45 + 5
,可以使用括号:
// NumberTypes/OpOrderParens.kt
fun main() {
println((45 + 5) * 6)
}
/* 输出:
300
*/
现在让我们来计算 体重指数(BMI),它是以千克为单位的体重除以身高的平方。如果你的 BMI 小于 18.5
,你体重过轻。在 18.5
到 24.9
之间是正常体重。BMI 为 25
及以上则是超重。此示例还展示了在函数的参数不能放在一行上时的首选格式化风格:
// NumberTypes/BMIMetric.kt
fun bmiMetric(
weight: Double,
height: Double
): String {
val bmi = weight / (height * height) // [1]
return if (bmi < 18.5) "体重过轻"
else if (bmi < 25) "正常体重"
else "超重"
}
fun main() {
val weight = 72.57 // 160 磅
val height = 1.727 // 68 英寸
val status = bmiMetric(weight, height)
println(status)
}
/* 输出:
正常体重
*/
- [1] 如果你移除了括号,你会先将
weight
除以height
,然后再将该结果乘以height
。这会得到一个更大的数字,但是答案是错误的。
bmiMetric()
使用 Double
类型来表示体重和身高。Double
可以存储非常大和非常小的浮点数。
下面是使用英制单位的版本,使用 Int
参数表示:
// NumberTypes/BMIEnglish.kt
fun bmiEnglish(
weight: Int,
height: Int
): String {
val bmi =
weight / (height * height) * 703.07 // [1]
return if (bmi < 18.5) "体重过轻"
else if (bmi < 25) "正常体重"
else "超重"
}
fun main() {
val weight = 160
val height = 68
val status = bmiEnglish(weight, height)
println(status)
}
/* 输出:
体重过轻
*/
为什么结果与使用 Double
的 bmiMetric()
不同?当你将一个整数除以另一个整数时,Kotlin 会生成一个整数结果。在整数除法中处理余数的标准方式是 截断,也就是“截掉并抛弃”(没有四舍五入)。所以,如果你将 5
除以 2
,会得到 2
,7/10
会得到 0
。当 Kotlin 在表达式 [1] 中计算 bmi
时,它将 160
除以 68 * 68
得到 0
。然后它将 0
乘以 703.07
得到 0
。
为了避免这个问题,将 703.07
移到计算的开头。这样会强制计算为 Double
类型:
val bmi = 703.07 * weight / (height * height)
在 bmiMetric()
中使用的 Double
参数可以防止这个问题。尽早将计算转换为所需类型,以保持准确性。
所有编程语言都有一个限制,不能存储超过整数范围的值。Kotlin 的 Int
类型
可以在 -231 到 +231-1 之间取值,这是 Int
32 位表示的限制。如果你对两个足够大的 Int
进行求和或乘法,你将会溢出结果:
// NumberTypes/IntegerOverflow.kt
fun main() {
val i: Int = Int.MAX_VALUE
println(i + i)
}
/* 输出:
-2
*/
Int.MAX_VALUE
是一个预定义的值,表示 Int
可以持有的最大值。
溢出会产生一个明显错误的结果,因为它既是负数,又比我们预期的要小得多。当 Kotlin 检测到潜在溢出时,会发出警告。
防止溢出是你作为开发者的责任。Kotlin 并不能在编译时总是检测到溢出,也不会阻止溢出,因为那会产生不可接受的性能影响。
如果你的程序包含大数字,你可以使用 Long
,它可以容纳从 -263 到 +263-1 的值。要定义一个 Long
类型的 val
,你可以显式地指定类型,或者在数字字面值末尾加上 L
,告诉 Kotlin 将该值视为 Long
:
// NumberTypes/LongConstants.kt
fun main() {
val i = 0 // 推断为 Int
val l1 = 0L // L 创建 Long
val l2: Long = 0 // 显式类型
println("$l1 $l2")
}
/* 输出:
0 0
*/
通过使用 Long
,我们防止了 IntegerOverflow.kt
中的溢出问题:
// NumberTypes/UsingLongs.kt
fun main() {
val i = Int.MAX_VALUE
println(0L + i + i) // [1]
println(1_000_000 * 1_000_000L) // [2]
}
/* 输出:
4294967294
1000000000000
*/
在 [1] 和 [2] 中使用数值字面值会强制进行 Long
计算,也会产生 Long
类型的结果。L
出现的位置并不重要。如果其中一个值是 Long
,则结果表达式也是 Long
。
尽管它们可以容纳比 Int
大得多的值,但 Long
仍然有大小限制:
// NumberTypes/BiggestLong.kt
fun main() {
println(Long.MAX_VALUE)
}
/* 输出:
9223372036854775807
*/
Long.MAX_VALUE
是 Long
可以容纳的最大值。
练习和解答可在 www.AtomicKotlin.com 找到。