数値
整数型
Kotlinには、数値を表現するための組み込み型が用意されています。 整数値には、サイズと値の範囲が異なる4つの型があります。
| 型 | サイズ (ビット) | 最小値 | 最大値 |
|---|---|---|---|
Byte | 8 | -128 | 127 |
Short | 16 | -32768 | 32767 |
Int | 32 | -2,147,483,648 (-231) | 2,147,483,647 (231 - 1) |
Long | 64 | -9,223,372,036,854,775,808 (-263) | 9,223,372,036,854,775,807 (263 - 1) |
符号付き整数型に加えて、Kotlinは符号なし整数型も提供しています。 符号なし整数は異なるユースケースを対象としているため、別個に説明します。 を参照してください。
明示的な型指定なしに変数を初期化すると、コンパイラは値を表現するのに十分な最小範囲の型をIntから自動的に推論します。Intの範囲を超えない場合、型はIntになります。その範囲を超える場合、型はLongになります。Long値を明示的に指定するには、値にサフィックスLを付加します。 ByteまたはShort型を使用するには、宣言で明示的に指定します。明示的な型指定は、指定された型の範囲を値が超えていないかをコンパイラにチェックさせます。
val one = 1 // Int
val threeBillion = 3000000000 // Long
val oneLong = 1L // Long
val oneByte: Byte = 1浮動小数点型
実数には、KotlinはIEEE 754標準に準拠する浮動小数点型FloatとDoubleを提供します。 FloatはIEEE 754の_単精度_を反映し、Doubleは_倍精度_を反映します。
これらの型はサイズが異なり、異なる精度の浮動小数点数を格納できます。
| 型 | サイズ (ビット) | 有効桁数 (ビット) | 指数部 (ビット) | 10進数の桁数 |
|---|---|---|---|---|
Float | 32 | 24 | 8 | 6-7 |
Double | 64 | 53 | 11 | 15-16 |
DoubleおよびFloat変数は、小数部を持つ数値でのみ初期化できます。 小数部と整数部はピリオド (.) で区切ります。
小数値で初期化された変数に対しては、コンパイラはDouble型を推論します。
val pi = 3.14 // Double
val one: Double = 1 // Int is inferred
// Initializer type mismatch
val oneDouble = 1.0 // Double値に対して明示的にFloat型を指定するには、サフィックスfまたはFを追加します。 この方法で指定された値が10進数で7桁を超える場合、丸められます。
val e = 2.7182818284 // Double
val eFloat = 2.7182818284f // Float, actual value is 2.7182817他のいくつかの言語とは異なり、Kotlinでは数値に対する暗黙的な拡大変換はありません。 たとえば、Doubleパラメータを持つ関数は、Double値でのみ呼び出すことができ、Float、Int、または他の数値では呼び出すことができません。
fun main() {
fun printDouble(x: Double) { print(x) }
val x = 1.0
val xInt = 1
val xFloat = 1.0f
printDouble(x)
printDouble(xInt)
// Argument type mismatch
printDouble(xFloat)
// Argument type mismatch
}数値型を異なる型に変換するには、明示的な数値変換を使用します。
数値リテラル
整数値にはいくつかの種類のリテラル定数があります。
- 10進数:
123 - 大文字の
Lで終わるLong型:123L - 16進数:
0x0F - 2進数:
0b00001011
Kotlinでは8進数リテラルはサポートされていません。
Kotlinは浮動小数点数の従来の表記法もサポートしています。
Double型 (小数部が文字で終わらない場合のデフォルト):123.5,123.5e10- 文字
fまたはFで終わるFloat型:123.5f
数値定数を読みやすくするためにアンダースコアを使用できます。
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符号なし整数リテラルには特殊なサフィックスもあります。 符号なし整数型のリテラルについて詳しくはこちらをご覧ください。
Java仮想マシンにおける数値のボックス化とキャッシュ
JVMが数値を格納する方法は、小さい (バイトサイズの) 数値にデフォルトで使用されるキャッシュのため、コードが直感に反する動作をする可能性があります。
JVMは数値をプリミティブ型 (int、doubleなど) として格納します。 ジェネリクスを使用したり、Int?のようなnull許容の数値参照を作成したりすると、数値はIntegerやDoubleのようなJavaクラスにボックス化されます。
JVMは、Integerや-128から127までの数値を表す他のオブジェクトにメモリ最適化手法を適用します。 そのようなオブジェクトへのすべてのnull許容参照は、同じキャッシュされたオブジェクトを参照します。 たとえば、以下のコードのnull許容オブジェクトは参照が等しくなります。
fun main() {
val a: Int = 100
val boxedA: Int? = a
val anotherBoxedA: Int? = a
println(boxedA === anotherBoxedA) // true
}この範囲外の数値の場合、null許容オブジェクトは異なりますが、構造的に等しくなります。
fun main() {
val b: Int = 10000
val boxedB: Int? = b
val anotherBoxedB: Int? = b
println(boxedB === anotherBoxedB) // false
println(boxedB == anotherBoxedB) // true
}このため、Kotlinはボックス化可能な数値とリテラルとの参照等価性の使用について、以下のメッセージで警告します: "Identity equality for arguments of types ... and ... is prohibited."Int、Short、Long、Byte型 (およびChar、Boolean) を比較する場合は、一貫した結果を得るために構造等価性チェックを使用してください。
明示的な数値変換
異なる表現のため、数値型は互いの_サブタイプではありません_。 結果として、より小さい型がより大きい型に暗黙的に変換されることは_なく_、その逆も同様です。 たとえば、Byte型の値をInt変数に代入するには、明示的な変換が必要です。
fun main() {
val byte: Byte = 1
// OK, literals are checked statically
val intAssignedByte: Int = byte
// Initializer type mismatch
val intConvertedByte: Int = byte.toInt()
println(intConvertedByte)
}すべての数値型は、他の型への変換をサポートしています。
toByte(): Byte(FloatおよびDoubleでは非推奨)toShort(): ShorttoInt(): InttoLong(): LongtoFloat(): FloattoDouble(): Double
多くの場合、型がコンテキストから推論され、算術演算子が変換を自動的に処理するようにオーバーロードされているため、明示的な変換は必要ありません。例:
fun main() {
val l = 1L + 3 // Long + Int => Long
println(l is Long) // true
}暗黙的な変換を行わない理由
Kotlinは暗黙的な変換をサポートしていません。これは、予期せぬ挙動につながる可能性があるためです。
異なる型の数値が暗黙的に変換された場合、等価性と同一性を静かに失うことがあります。 たとえば、IntがLongのサブタイプであったと想像してみてください。
// Hypothetical code, does not actually compile:
val a: Int? = 1 // A boxed Int (java.lang.Integer)
val b: Long? = a // Implicit conversion yields a boxed Long (java.lang.Long)
print(b == a) // Prints "false" as Long.equals() checks not only the value but whether the other number is Long as well数値演算
Kotlinは数値に対する標準的な算術演算子 (+、-、*、/、%) をサポートしています。これらは適切なクラスのメンバーとして宣言されています。
fun main() {
println(1 + 2)
println(2_500_000_000L - 1L)
println(3.14 * 2.71)
println(10.0 / 3)
}これらの演算子は、カスタム数値クラスでオーバーライドできます。 詳細は演算子オーバーロードを参照してください。
整数除算
整数同士の除算は常に整数を返します。小数部はすべて破棄されます。
fun main() {
val x = 5 / 2
println(x == 2.5)
// Operator '==' cannot be applied to 'Int' and 'Double'
println(x == 2)
// true
}これは、任意の2つの整数型間の除算に当てはまります。
fun main() {
val x = 5L / 2
println (x == 2)
// Error, as Long (x) cannot be compared to Int (2)
println(x == 2L)
// true
}小数部を含む除算結果を返すには、引数のいずれかを明示的に浮動小数点型に変換します。
fun main() {
val x = 5 / 2.toDouble()
println(x == 2.5)
}ビット演算
Kotlinは整数に対する_ビット演算_のセットを提供します。これらは、数値の表現のビットと直接バイナリレベルで動作します。 ビット演算は、中置記法で呼び出すことができる関数として表現されます。これらはIntとLongにのみ適用できます。
fun main() {
val x = 1
val xShiftedLeft = (x shl 2)
println(xShiftedLeft)
// 4
val xAnd = x and 0x000FF000
println(xAnd)
// 0
}ビット演算の完全なリスト:
shl(bits)– 符号付き左シフトshr(bits)– 符号付き右シフトushr(bits)– 符号なし右シフトand(bits)– ビットごとのANDor(bits)– ビットごとのORxor(bits)– ビットごとのXORinv()– ビットごとの反転
浮動小数点数の比較
このセクションで説明する浮動小数点数の演算は次のとおりです。
- 等価性チェック:
a == bおよびa != b - 比較演算子:
a < b、a > b、a <= b、a >= b - 範囲のインスタンス化と範囲チェック:
a..b、x in a..b、x !in a..b
オペランドaとbがFloatまたはDouble、あるいはそのnull許容の対応物 (型が宣言されているか、推論されているか、スマートキャストの結果である場合) であると静的に判明している場合、数値に対する演算とそれらが形成する範囲は、IEEE 754 浮動小数点算術標準に従います。
ただし、汎用的なユースケースをサポートし、全順序を提供するために、浮動小数点数として静的に型付けされていないオペランドについては動作が異なります。たとえば、Any、Comparable<...>、またはCollection<T>型などです。この場合、演算はFloatおよびDoubleのequalsおよびcompareToの実装を使用します。その結果、次のようになります。
NaNはそれ自身と等しいと見なされるNaNはPOSITIVE_INFINITYを含む他のどの要素よりも大きいと見なされる-0.0は0.0よりも小さいと見なされる
ここでは、浮動小数点数として静的に型付けされたオペランド (Double.NaN) と、浮動小数点数として静的に型付けされていないオペランド (listOf(T)) の間の動作の違いを示す例を示します。
fun main() {
// Operand statically typed as floating-point number
println(Double.NaN == Double.NaN) // false
// Operand NOT statically typed as floating-point number
// So NaN is equal to itself
println(listOf(Double.NaN) == listOf(Double.NaN)) // 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]
}