Kotlin 基本数据类型(一):Numbers

Kotlin 基本数据类型(一):概述及分类

整数类型

Kotlin 内置了几种类型来表示数字,有以下几种类型来表示整数:

Type Size (bits) Min value Max value
Byte 8 -128 127
Short 16 -32768 32767
Int 32 -2,147,483,648 2,147,483,647
Long 64 -9,223,372,036,854,775,808 9,223,372,036,854,775,807

如果在初始化变量的时候,没有指定具体的类型。那么编译器会自动推断类型,只不过会从Int类型开始往上推断。如果说我们声明的变量超过了Int的最小值和最大值,那么就会推断为Long。如果没有超过Int类型的范围,那就是Int。

所以,只要我们声明的整数在-2,147,483,648 到 2,147,483,647范围内,只要没指定类型,那就是Int类型。

如果我们想要明确为Long类型,只需要在数字后面加上L就行。如果我们想要使用 Byte 或者 Short 类型,就需要在声明的时候明确声明出类型来。

只要我们明确声明了类型,就会触发编译器检查我们给的数字是否在类型的范围内。

kotlin 复制代码
val one = 1 // Int  没有明确类型,推断为Int
val threeBillion = 3000000000 // Long 超过了Int的范围,推断为Long
val oneLong = 1L // Long 添加L后缀,强制为Long
val oneByte: Byte = 1 // 声明为Byte,强制为Byte

浮点类型

对于实数来说,Kotlin 提供了遵循 IEEE 754 标准的浮点类型 Float 和 Double,Float是单精度,Double是双精度。

两种类型的比较如下:

Type Size (bits) Significant bits Exponent bits Decimal digits
Float 32 24 8 6-7
Double 64 53 11 15-16

简单来说:Float类型能表示的十进制有效数字位数大概是 6 - 7 位;Double类型能表示的十进制有效数字位数大概是 15 - 16 位

对我们开发者来说,我们只能用小数来初始化 Double 和 Float 类型的变量,并且编译器类型推断小数是Double类型。

kotlin 复制代码
val pi = 3.14          // Double

val one: Double = 1    // Int is inferred  编译器会报错 只能使用小数来初始化Double
// Initializer type mismatch

val oneDouble = 1.0    // Double  编译器推断为Doube

如果我们开发者想要声明Float类型,那么只需要在小数后面加上f或者F即可,如果一个超过7为小数的变量,被强制声明为F,那么精度会缺失:

kotlin 复制代码
val e = 2.7182818284          // Double
val eFloat = 2.7182818284f    // Float, actual value is 2.7182817
// 2.7182818284小数超过了7位,强制为Float类型,那么真实的数值就会变成2.7182817。

和其他编程语言不同,Kotlin没有隐式的类型转换。在其他语言中,小范围的数字可以放在大范围的数字里面,比如int到long。

但在Kotlin中,这是不允许的。如果一个方法接受一个Doube类型的参数,那么只可以传入Doube,传入Float,Int或者其他数字类型都是不被允许的。

kotlin 复制代码
fun printDouble(x: Double) { print(x) }

val x = 1.0
val xInt = 1    
val xFloat = 1.0f 

printDouble(x) //正确,x推断为Doubel

printDouble(xInt)   //错误,xInt推断为Int
// Argument type mismatch

printDouble(xFloat)//错误,xFloat强制为Float
// Argument type mismatch

number字面常量

整型值的字面常量有以下几种类型:

  • 十进制:123
  • 长整数以大写字母 L 结尾:123L
  • 十六进制:0x0F
  • 二进制:0b00001011

Kotlin 也支持浮点数的常规表示法:

  • 双精度浮点数(当小数部分不以字母结尾时默认为此类型):123.5、123.5e10
  • 单精度浮点数,以字母 f 或 F 结尾:123.5f

也可以使用下划线来让数字常量更易读:

kotlin 复制代码
val oneMillion = 1_000_000 
val creditCardNumber = 1234_5678_9012_3456L 
val socialSecurityNumber = 999_99_9999L 
val hexBytes = 0xFF_EC_DE_5E 
val bytes = 0b11010010_01101001_10010100_10010010 
val bigFractional = 1_234_567.7182818284

JVM 数字类型装箱和缓存

因为 JVM 会缓存字节大小的数字,所以有一些代码可能会和开发者预期有所不同。

JVM 会将数字存储类原始的类型:int、doubel等等。但是假如我们使用了泛型或者创建了可空的数字引用比如Int?,数字会被包装为包装类型,比如Integer。

JVM 对表示 -128 到 127 之间数字的 Integer 及其他数值对象采用了一种内存优化技术。所有指向这类对象的可空引用都会指向同一个缓存对象。例如,以下代码中的可空对象在引用上是相等的:

kotlin 复制代码
val a: Int = 100
val boxedA: Int? = a
val anotherBoxedA: Int? = a

println(boxedA === anotherBoxedA) // true

如果超过了-128到127,可空对象的引用就是不用的了,但是结构是相同的。

kotlin 复制代码
val b: Int = 10000
val boxedB: Int? = b
val anotherBoxedB: Int? = b

println(boxedB === anotherBoxedB) // false  超过了范围,所以地址不用
println(boxedB == anotherBoxedB) // true   超过了范围,结构相同,也就是equal相等。

鉴于此,当我们开发者使用包装类型和字面类型进行比较的时候,就会有警告信息: "Identity equality for arguments of types ... and ... is prohibited.",所以在比较Int, Short, Long, 和 Byte 类型 (包括 Char 和 Boolean)时,使用结构性相等比较会有更加稳定的结果。

显示转换

由于表示方式不同,数字类型之间不存在父子类型关系。因此,较小的类型不会隐式转换为较大的类型,反之亦然。例如,将 Byte 类型的值赋给 Int 变量需要显式转换:

kotlin 复制代码
val byte: Byte = 1   //确定为byte类型
// OK, literals are checked statically

val intAssignedByte: Int = byte  //编译器报错  小类型无法给大类型
// Initializer type mismatch

val intConvertedByte: Int = byte.toInt()  //显示的类型转换

println(intConvertedByte)

数字类型之间支持相互显示转换:

toByte(): Byte ( Float and Double 已经弃用)

toShort(): Short

toInt(): Int

toLong(): Long

toFloat(): Float

toDouble(): Double

不过,在很多情况下,不需要进行显式类型转换,因为类型会从上下文推断得出,而且算术运算符经过重载可以自动处理转换。例如:

kotlin 复制代码
val l = 1L + 3       // Long + Int => Long
println(l is Long)   // true

不支持隐式转换的考量

Kotlin 不支持隐式转换是因为防止出现意外的行为。

如果不同类型可以隐式转换,可能有的时候比较相等会出现意想不到的结果。比如,int和long。

kotlin 复制代码
val a: Int? = 1   
val b: Long? = a   
print(b == a)      // Prints "false" 

因为Long在比较的时候,会先比较是否是Long类型

number 运算

Kotlin支持标准的数字运算:+, -, *, /, %,这些运算被包装成了成员函数。

kotlin 复制代码
println(1 + 2)
println(2_500_000_000L - 1L)
println(3.14 * 2.71)
println(10.0 / 3)

当然了,开发者也可以在自定义的数字类中重载这些运算。

整数除法

整数之前相除得到的结果也是整数,小数部分会被舍弃。

kotlin 复制代码
val x = 5 / 2
println(x == 2.5) 
// Operator '==' cannot be applied to 'Int' and 'Double'

println(x == 2)   
// true

想要得到小数的结果,只需要两个整数中的任意一个变成浮点数即可。

位运算

Kotlin 为整数提供了一系列的位运算操作,这些运算直接对数字表示的二进制位进行操作。位运算使用中缀形式的函数来表示。

kotlin 复制代码
fun main() {
//sampleStart
    val x = 1
    val xShiftedLeft = (x shl 2)
    println(xShiftedLeft)
    // 4

    val xAnd = x and 0x000FF000
    println(xAnd)
    // 0
//sampleEnd
}
  • shl(bits) -- 带符号左移
  • shr(bits) -- 带符号右移
  • ushr(bits) -- 无符号右移
  • and(bits) -- 按位与
  • or(bits) -- 按位或
  • xor(bits) -- 按位异或
  • inv() -- 按位取反

浮点数比较

本节讨论的浮点数操作包括:

相等性检查: a == b and a != b

比较运算: a < b, a > b, a <= b, a >= b

范围实例和检查: a..b, x in a..b, x !in a..b

当操作数 a 和 b 在静态类型上明确为 Float 或 Double,或它们的可空对应类型(类型已声明、推断或为智能类型转换的结果)时,这些数字上的操作及其形成的范围遵循 IEEE 754 浮点数算术标准。

然而,为了支持泛型场景并提供全序关系,对于静态类型非浮点数的操作数(例如 Any、Comparable<...> 或 Collection 类型),行为会有所不同。在这种情况下,操作会使用 Float 和 Double 的 equals 和 compareTo 实现。因此:

  • NaN 被视为与自身相等
  • NaN 被视为大于任何其他元素,包括 POSITIVE_INFINITY(正无穷)
  • 0.0 被视为小于 0.0

下面的示例展示了静态类型为浮点数的操作数(Double.NaN)与静态类型非浮点数的操作数(listOf(T))之间的行为差异:

kotlin 复制代码
    // Operand statically typed as floating-point number
println(Double.NaN == Double.NaN)                 // false  

//Double.NaN == Double.NaN 结果为 false,是因为 NaN(Not a Number,非数值)在 IEEE 754 浮点数标准中的特殊定义。
//IEEE 754 标准明确规定:NaN 与任何值(包括它自身)都不相等。这是因为 NaN 代表 "无效的数值结果"(例如 0/0、sqrt (-1) 等运算的结果),而逻辑上 "两个无效结果是否相等" 是无法定义的。因此,标准强制规定 NaN 与自身的相等性检查返回 false。

// Operand NOT statically typed as floating-point number
// So NaN is equal to itself
println(listOf(Double.NaN) == listOf(Double.NaN)) // true
//基本类型的 NaN 比较遵循 IEEE 754 标准(不等),而包装类型的 NaN 通过 equals() 比较时被视为相等,列表比较依赖后者,因此结果为 true

// Operand statically typed as floating-point number
println(0.0 == -0.0)                              // true

// Operand NOT statically typed as floating-point number
// So -0.0 is less than 0.0
println(listOf(0.0) == listOf(-0.0))              // false

println(listOf(Double.NaN, Double.POSITIVE_INFINITY, 0.0, -0.0).sorted())
// [-0.0, 0.0, Infinity, NaN]

原文链接

相关推荐
小小愿望8 分钟前
JavaScript生成随机数的全面指南:别再只会用Math.random()了!
前端
Jackson__15 分钟前
RAG究竟是什么?一文搞懂大模型的记忆增强术
前端
前端市界17 分钟前
前端视角: PyQt6+Vue3 跨界开发实战
前端·qt·pyqt
阅文作家助手开发团队_山神19 分钟前
第四章:Flutter自定义Engine本地依赖与打包流程
前端·flutter
JuneXcy22 分钟前
11.web api 2
前端·javascript·html
zYear27 分钟前
Elpis 全栈应用框架-- 总结
前端·javascript
Juchecar1 小时前
分析:将现代开源浏览器的JavaScript引擎更换为Python的可行性与操作
前端·javascript·python
极客小俊1 小时前
Font Awesome 一个基于CSS和LESS的免费图标库工具包
前端
yinuo1 小时前
CSS基础动画keyframes
前端