【Kotlin】kotlin.math.roundでつまずかない!よくあるエラーと解決策

2025-06-06

Kotlinプログラミング言語における kotlin.math.round 関数は、数値を最も近い整数に丸めるために使用されます。数学的な丸め規則に従い、.5 の場合は次の偶数に丸めるのではなく、常に遠い方に丸める(ゼロから離れる方向に丸める)という点で、他の丸め関数と異なる場合があります。

kotlin.math パッケージに含まれており、使用する際には明示的にインポートするか、あるいはIDEが自動でインポートすることが多いです。

基本的な機能

kotlin.math.round は、引数として与えられた浮動小数点数(Float または Double)を最も近い整数に丸めます。

  • .5 以外の場合:通常の四捨五入と同じように丸められます。
    • 例: 2.32 に、2.73 に丸められます。
  • X.5 の場合:正の数の場合は切り上げ、負の数の場合は切り捨てとなります(つまり、ゼロから遠ざかる方向に丸められます)。
    • 例: 2.53 に、-2.5-3 に丸められます。

オーバーロードされた関数

kotlin.math.round には、主に以下のオーバーロードされたバージョンがあります。

    • Float 型の引数を取り、丸めた結果を Float 型で返します。
    • 例: round(3.14f)3.0f を返します。
  1. fun round(a: Double): Double

    • Double 型の引数を取り、丸めた結果を Double 型で返します。
    • 例: round(3.14)3.0 を返します。
  2. fun round(a: Float): Int

    • Float 型の引数を取り、丸めた結果を Int 型で返します。これは roundToInt() と同等です。
    • 例: round(3.14f)3 を返します。
  3. fun round(a: Double): Long

    • Double 型の引数を取り、丸めた結果を Long 型で返します。これは roundToLong() と同等です。
    • 例: round(3.14)3L を返します。

これらのうち、3と4は実際には roundToInt() および roundToLong() という拡張関数として提供されていますが、kotlin.math.round という文脈で丸め結果を整数型で取得する一般的な方法として理解しておくと良いでしょう。

使用例

import kotlin.math.round
import kotlin.math.roundToInt
import kotlin.math.roundToLong

fun main() {
    val num1 = 2.3f
    val num2 = 2.7f
    val num3 = 2.5f
    val num4 = -2.3f
    val num5 = -2.7f
    val num6 = -2.5f

    println("--- round(Float): Float ---")
    println("round($num1) = ${round(num1)}") // 2.0
    println("round($num2) = ${round(num2)}") // 3.0
    println("round($num3) = ${round(num3)}") // 3.0 (正の0.5は切り上げ)
    println("round($num4) = ${round(num4)}") // -2.0
    println("round($num5) = ${round(num5)}") // -3.0
    println("round($num6) = ${round(num6)}") // -3.0 (負の0.5は切り捨て、ゼロから遠ざかる)

    println("\n--- round(Double): Double ---")
    val doubleNum1 = 5.6
    val doubleNum2 = 5.4
    val doubleNum3 = 5.5
    val doubleNum4 = -5.5
    println("round($doubleNum1) = ${round(doubleNum1)}") // 6.0
    println("round($doubleNum2) = ${round(doubleNum2)}") // 5.0
    println("round($doubleNum3) = ${round(doubleNum3)}") // 6.0
    println("round($doubleNum4) = ${round(doubleNum4)}") // -6.0

    println("\n--- roundToInt() (Float -> Int) ---")
    println("roundToInt($num1) = ${num1.roundToInt()}") // 2
    println("roundToInt($num3) = ${num3.roundToInt()}") // 3

    println("\n--- roundToLong() (Double -> Long) ---")
    println("roundToLong($doubleNum1) = ${doubleNum1.roundToLong()}") // 6
    println("roundToLong($doubleNum3) = ${doubleNum3.roundToLong()}") // 6
}


kotlin.math.round関数は非常にシンプルですが、いくつかの一般的な落とし穴や誤解があります。ここでは、それらのエラーとトラブルシューティングの方法について説明します。

戻り値の型に関する誤解

エラーの症状
round()関数の結果が期待する整数型(IntLong)ではなく、FloatDouble型で返ってきてしまう。

原因
kotlin.math.round(a: Float): Floatkotlin.math.round(a: Double): Double のオーバーロードを使用しているためです。これらの関数は、入力と同じ浮動小数点型の値を返します。


import kotlin.math.round

fun main() {
    val floatValue = 3.7f
    val roundedFloat = round(floatValue) // roundedFloat は Float 型 (3.0f)
    println(roundedFloat::class.simpleName) // Float

    val doubleValue = 5.2
    val roundedDouble = round(doubleValue) // roundedDouble は Double 型 (5.0)
    println(roundedDouble::class.simpleName) // Double
}

トラブルシューティング
結果を整数型で取得したい場合は、拡張関数であるroundToInt()またはroundToLong()を使用します。

import kotlin.math.roundToInt
import kotlin.math.roundToLong

fun main() {
    val floatValue = 3.7f
    val roundedInt = floatValue.roundToInt() // roundedInt は Int 型 (3)
    println(roundedInt::class.simpleName) // Int

    val doubleValue = 5.2
    val roundedLong = doubleValue.roundToLong() // roundedLong は Long 型 (5L)
    println(roundedLong::class.simpleName) // Long
}

.5の丸め規則に関する誤解

エラーの症状
.5で終わる数値の丸め結果が、一般的な「四捨五入」(日本の義務教育で習う小数点第一位を四捨五入するルール)と異なる。特に、負の数の場合に混乱が生じやすい。

原因
kotlin.math.roundは「0から遠ざかる方向に丸める (round half away from zero)」という規則を採用しています。これは、銀行型丸め(JIS丸め、偶数丸め)や一般的な四捨五入とは異なる場合があります。

  • -2.5-3.0 に丸められます。(0から遠ざかる)
  • 2.53.0 に丸められます。(0から遠ざかる)


import kotlin.math.round

fun main() {
    println("round(2.5f) = ${round(2.5f)}")   // 3.0f
    println("round(-2.5f) = ${round(-2.5f)}") // -3.0f
}

トラブルシューティング
これはエラーではなく、round関数の仕様です。もし異なる丸め規則(例: 偶数丸めや常に切り上げ/切り捨てなど)が必要な場合は、以下の代替関数を検討してください。

  • カスタム丸めロジック: 特定の要件(例: 銀行型丸め)がある場合は、BigDecimalクラスのsetScaleメソッドとRoundingModeを組み合わせるなど、より複雑な丸めロジックを実装する必要があります。

    import java.math.BigDecimal
    import java.math.RoundingMode
    
    fun main() {
        val num = 2.5
        val bd = BigDecimal(num.toString()) // DoubleからBigDecimalへの変換は文字列経由が安全
        val roundedHalfEven = bd.setScale(0, RoundingMode.HALF_EVEN) // 銀行型丸め (2.5 -> 2, 3.5 -> 4)
        println("BigDecimal HALF_EVEN (2.5 -> 2) = $roundedHalfEven")
    
        val numNeg = -2.5
        val bdNeg = BigDecimal(numNeg.toString())
        val roundedHalfEvenNeg = bdNeg.setScale(0, RoundingMode.HALF_EVEN) // -2.5 -> -2
        println("BigDecimal HALF_EVEN (-2.5 -> -2) = $roundedHalfEvenNeg")
    }
    
  • truncate(a: Double): Double / truncate(a: Float): Float: 小数点以下を切り捨てて整数部のみを取得(ゼロ方向への丸め)。例: 2.9 -> 2.0, -2.9 -> -2.0

  • floor(a: Double): Double / floor(a: Float): Float: 常に切り捨て(床関数)。例: 2.1 -> 2.0, 2.9 -> 2.0, -2.1 -> -3.0

  • ceil(a: Double): Double / ceil(a: Float): Float: 常に切り上げ(天井関数)。例: 2.1 -> 3.0, 2.9 -> 3.0, -2.1 -> -2.0

浮動小数点数の精度問題

エラーの症状
非常に大きな、または非常に小さな浮動小数点数を丸めようとしたときに、期待しない結果になることがある。これはround関数自体の問題というより、浮動小数点数(Float/Double)の一般的な性質によるものです。

原因
浮動小数点数は、内部的には2進数で表現されるため、一部の10進数を正確に表現できません(例: 0.1)。これにより、丸め処理の前にすでにわずかな誤差が生じている場合があります。


0.1 + 0.20.3 にならず、0.30000000000000004 のようになる場合があるように、X.5 と思われる値が実際には X.4999999999999999X.5000000000000001 となっていることがあります。

トラブルシューティング
財務計算や高い精度が要求される計算では、FloatDoubleではなく、java.math.BigDecimalを使用することを強く推奨します。BigDecimalは任意の精度で10進数を表現できるため、浮動小数点数の精度問題を回避できます。

import java.math.BigDecimal
import java.math.RoundingMode
import kotlin.math.roundToInt // roundToIntはFloat/Doubleの拡張関数なので、BigDecimalには直接使えない

fun main() {
    val impreciseDouble = 2.4999999999999996 // 実際には2.5に近いが、内部的には2.5未満
    println("round($impreciseDouble) = ${kotlin.math.round(impreciseDouble)}") // 2.0 (roundToIntでも同じ)

    val preciseBigDecimal = BigDecimal("2.5") // 文字列で初期化することで正確な値を保持
    val roundedBigDecimal = preciseBigDecimal.setScale(0, RoundingMode.HALF_UP) // 通常の四捨五入
    println("BigDecimal round(2.5) = $roundedBigDecimal") // 3
}

kotlin.math.roundを使用する際には、以下の点を念頭に置いてください。

  1. 戻り値の型: 整数型が必要な場合は、roundToInt()roundToLong()を使用する。
  2. .5の丸め規則: 0から遠ざかる方向に丸めるという仕様を理解する。異なる規則が必要な場合は、ceilfloortruncate、またはBigDecimalを検討する。
  3. 浮動小数点数の精度: 精度が重要な計算では、BigDecimalの使用を検討する。


kotlin.math.round関数は、数値を最も近い整数に丸めるために使われます。ここでは、基本的な使い方から、戻り値の型、そして .5 の丸め規則、他の丸め関数との比較まで、具体的なコード例を挙げて詳しく解説します。

まず、kotlin.mathパッケージの関数を使用するためには、通常、以下のインポートが必要になります。IDEが自動的に追加してくれることも多いです。

import kotlin.math.round     // round(Float): Float, round(Double): Double
import kotlin.math.roundToInt // Float.roundToInt(): Int
import kotlin.math.roundToLong // Double.roundToLong(): Long
import kotlin.math.ceil       // 切り上げ (ceiling)
import kotlin.math.floor      // 切り捨て (floor)
import kotlin.math.truncate   // ゼロ方向への切り捨て (truncate)

round() の基本的な使い方と戻り値の型 (Float / Double)

round()関数は、引数として与えられたFloatまたはDoubleの値を、丸めた結果として同じ浮動小数点型で返します。

fun main() {
    println("--- round(Float/Double) の基本的な使い方 ---")

    val floatVal1 = 3.14f
    val roundedFloat1 = round(floatVal1) // round(Float) -> Float
    println("round($floatVal1) = $roundedFloat1 (型: ${roundedFloat1::class.simpleName})") // 3.0 (Float)

    val floatVal2 = 3.7f
    val roundedFloat2 = round(floatVal2)
    println("round($floatVal2) = $roundedFloat2 (型: ${roundedFloat2::class.simpleName})") // 4.0 (Float)

    val doubleVal1 = 5.2
    val roundedDouble1 = round(doubleVal1) // round(Double) -> Double
    println("round($doubleVal1) = $roundedDouble1 (型: ${roundedDouble1::class.simpleName})") // 5.0 (Double)

    val doubleVal2 = 5.8
    val roundedDouble2 = round(doubleVal2)
    println("round($doubleVal2) = $roundedDouble2 (型: ${roundedDouble2::class.simpleName})") // 6.0 (Double)

    val doubleVal3 = -4.3
    val roundedDouble3 = round(doubleVal3)
    println("round($doubleVal3) = $roundedDouble3 (型: ${roundedDouble3::class.simpleName})") // -4.0 (Double)
}

解説

  • 戻り値の型は、引数の型(FloatまたはDouble)と同じになります。
  • round(5.2)5.0 を、round(5.8)6.0 を返します。
  • round(3.14f)3.0f を、round(3.7f)4.0f を返します。

結果を整数型で取得する (roundToInt() / roundToLong())

丸めた結果をIntLongといった整数型で直接取得したい場合は、roundToInt()またはroundToLong()拡張関数を使用します。これらはround()の後に.toInt().toLong()を呼び出すのと同じような振る舞いをしますが、より簡潔で推奨されます。

fun main() {
    println("\n--- 結果を整数型で取得する ---")

    val floatNum = 7.6f
    val intResult = floatNum.roundToInt() // Float.roundToInt() -> Int
    println("$floatNum.roundToInt() = $intResult (型: ${intResult::class.simpleName})") // 8 (Int)

    val doubleNum = 123.45
    val longResult = doubleNum.roundToLong() // Double.roundToLong() -> Long
    println("$doubleNum.roundToLong() = $longResult (型: ${longResult::class.simpleName})") // 123 (Long)

    val negativeDouble = -10.9
    val negativeIntResult = negativeDouble.roundToInt()
    println("$negativeDouble.roundToInt() = $negativeIntResult (型: ${negativeIntResult::class.simpleName})") // -11 (Int)
}

解説

  • roundToInt()Float から Int へ、roundToLong()Double から Long へ変換します。
  • 123.45.roundToLong()123L を返します。
  • 7.6f.roundToInt()8 を返します。

.5 の丸め規則 (round half away from zero)

kotlin.math.round の特徴として、.5 で終わる値は「ゼロから遠ざかる方向に丸められる (round half away from zero)」という規則があります。これは、一般的な四捨五入(例えば日本では2.53-2.5-2とするルール)とは異なる場合があるので注意が必要です。

fun main() {
    println("\n--- .5 の丸め規則 (round half away from zero) ---")

    val posHalfFloat = 2.5f
    println("round($posHalfFloat) = ${round(posHalfFloat)}") // 3.0f (0から遠ざかる)
    println("$posHalfFloat.roundToInt() = ${posHalfFloat.roundToInt()}") // 3

    val negHalfFloat = -2.5f
    println("round($negHalfFloat) = ${round(negHalfFloat)}") // -3.0f (0から遠ざかる)
    println("$negHalfFloat.roundToInt() = ${negHalfFloat.roundToInt()}") // -3

    val posHalfDouble = 3.5
    println("round($posHalfDouble) = ${round(posHalfDouble)}") // 4.0 (0から遠ざかる)

    val negHalfDouble = -3.5
    println("round($negHalfDouble) = ${round(negHalfDouble)}") // -4.0 (0から遠ざかる)
}

解説

  • この挙動は roundToInt()roundToLong() を使っても同じです。
  • 2.5f3.0f に、-2.5f-3.0f に丸められます。

他の丸め関数との比較

Kotlinのkotlin.mathパッケージには、round以外にも様々な丸め関数があります。目的に応じて使い分けることが重要です。

  • truncate: 小数点以下を切り捨てて整数部のみを取得(ゼロ方向への切り捨て)。
  • floor (floor): 常に切り捨て(床関数)。
  • ceil (ceiling): 常に切り上げ(天井関数)。
  • round: 最も近い整数へ丸める (.5はゼロから遠ざかる)。
fun main() {
    println("\n--- 他の丸め関数との比較 ---")

    val num = 3.7
    val negNum = -3.7

    println("数値: $num")
    println("  round($num)    = ${round(num)}")    // 4.0
    println("  ceil($num)     = ${ceil(num)}")     // 4.0 (切り上げ)
    println("  floor($num)    = ${floor(num)}")    // 3.0 (切り捨て)
    println("  truncate($num) = ${truncate(num)}") // 3.0 (ゼロ方向へ切り捨て)

    println("\n数値: $negNum")
    println("  round($negNum)    = ${round(negNum)}")    // -4.0
    println("  ceil($negNum)     = ${ceil(negNum)}")     // -3.0 (切り上げ)
    println("  floor($negNum)    = ${floor(negNum)}")    // -4.0 (切り捨て)
    println("  truncate($negNum) = ${truncate(negNum)}") // -3.0 (ゼロ方向へ切り捨て)

    val numHalf = 3.5
    val negNumHalf = -3.5

    println("\n数値: $numHalf")
    println("  round($numHalf)    = ${round(numHalf)}")    // 4.0 (ゼロから遠ざかる)
    println("  ceil($numHalf)     = ${ceil(numHalf)}")     // 4.0
    println("  floor($numHalf)    = ${floor(numHalf)}")    // 3.0
    println("  truncate($numHalf) = ${truncate(numHalf)}") // 3.0

    println("\n数値: $negNumHalf")
    println("  round($negNumHalf)    = ${round(negNumHalf)}")    // -4.0 (ゼロから遠ざかる)
    println("  ceil($negNumHalf)     = ${ceil(negNumHalf)}")     // -3.0
    println("  floor($negNumHalf)    = ${floor(negNumHalf)}")    // -4.0
    println("  truncate($negNumHalf) = ${truncate(negNumHalf)}") // -3.0
}

解説

  • truncate() は、3.73.0 に、-3.7-3.0 に(ゼロ方向へ)丸めます。これは小数点以下を単純に切り捨てる挙動です。
  • floor() は、3.73.0 に、-3.7-4.0 に(より小さな整数へ)丸めます。
  • ceil() は、3.74.0 に、-3.7-3.0 に(より大きな整数へ)丸めます。

精度が重要な計算 (BigDecimal)

金融計算など、浮動小数点数の精度が極めて重要になる場合は、kotlin.math.roundなどのFloatDoubleベースの関数ではなく、java.math.BigDecimalを使用することを強く推奨します。BigDecimalは任意の精度で10進数を表現でき、さまざまな丸めモードをサポートしています。

import java.math.BigDecimal
import java.math.RoundingMode

fun main() {
    println("\n--- BigDecimal を使用した精度計算 ---")

    val value = BigDecimal("2.5") // 文字列で初期化することで正確な値を保持
    val negValue = BigDecimal("-2.5")

    // HALF_UP: 通常の四捨五入 (0.5は切り上げ)
    val roundedHalfUp = value.setScale(0, RoundingMode.HALF_UP)
    val roundedHalfUpNeg = negValue.setScale(0, RoundingMode.HALF_UP)
    println("2.5 with HALF_UP: $roundedHalfUp")     // 3
    println("-2.5 with HALF_UP: $roundedHalfUpNeg") // -3

    // HALF_DOWN: 0.5は切り捨て
    val roundedHalfDown = value.setScale(0, RoundingMode.HALF_DOWN)
    val roundedHalfDownNeg = negValue.setScale(0, RoundingMode.HALF_DOWN)
    println("2.5 with HALF_DOWN: $roundedHalfDown")     // 2
    println("-2.5 with HALF_DOWN: $roundedHalfDownNeg") // -2

    // HALF_EVEN: 銀行型丸め (0.5は最も近い偶数へ丸める)
    val value3_5 = BigDecimal("3.5")
    val value2_5 = BigDecimal("2.5")
    val roundedHalfEven3_5 = value3_5.setScale(0, RoundingMode.HALF_EVEN)
    val roundedHalfEven2_5 = value2_5.setScale(0, RoundingMode.HALF_EVEN)
    val roundedHalfEvenNeg3_5 = BigDecimal("-3.5").setScale(0, RoundingMode.HALF_EVEN)
    val roundedHalfEvenNeg2_5 = BigDecimal("-2.5").setScale(0, RoundingMode.HALF_EVEN)
    println("3.5 with HALF_EVEN: $roundedHalfEven3_5") // 4
    println("2.5 with HALF_EVEN: $roundedHalfEven2_5") // 2
    println("-3.5 with HALF_EVEN: $roundedHalfEvenNeg3_5") // -4
    println("-2.5 with HALF_EVEN: $roundedHalfEvenNeg2_5") // -2
}
  • RoundingModeには様々な丸めモードがあり、kotlin.math.roundの挙動(HALF_UPHALF_DOWNの中間的な振る舞いですが、HALF_UPに近いです)や、より厳密な銀行型丸め(HALF_EVEN)などを指定できます。
  • setScale(0, RoundingMode)メソッドを使って、小数点以下を丸めます。0は小数点以下0桁に丸めることを意味します。
  • BigDecimalを使用する場合、初期化は文字列で行うのが最も安全です(Doubleから変換すると既に誤差が生じている可能性があるため)。


kotlin.math.roundは数値を最も近い整数に丸める便利な関数ですが、特定の丸め規則や要件がある場合には、他の代替手法を検討する必要があります。ここでは、そのような代替手法について、Kotlinのコード例を交えて解説します。

kotlin.mathパッケージ内の他の丸め関数

kotlin.mathパッケージには、round以外にも様々な丸め関数があり、それぞれ異なる丸め規則を持っています。

  • truncate(a: Double): Double / truncate(a: Float): Float: ゼロ方向への切り捨て 小数点以下を単純に切り捨て、整数部分のみを取得します。符号は保持されます。

    import kotlin.math.truncate
    
    fun main() {
        println("\n--- truncate (ゼロ方向への切り捨て) ---")
        println("truncate(3.1) = ${truncate(3.1)}")   // 3.0
        println("truncate(3.9) = ${truncate(3.9)}")   // 3.0
        println("truncate(-3.1) = ${truncate(-3.1)}") // -3.0
        println("truncate(-3.9) = ${truncate(-3.9)}") // -3.0
    }
    
  • floor(a: Double): Double / floor(a: Float): Float: 切り捨て(床関数) 常に値をより小さな整数に丸めます。

    import kotlin.math.floor
    
    fun main() {
        println("\n--- floor (切り捨て) ---")
        println("floor(3.1) = ${floor(3.1)}")   // 3.0
        println("floor(3.9) = ${floor(3.9)}")   // 3.0
        println("floor(3.0) = ${floor(3.0)}")   // 3.0
        println("floor(-3.1) = ${floor(-3.1)}") // -4.0
        println("floor(-3.9) = ${floor(-3.9)}") // -4.0
    }
    
  • ceil(a: Double): Double / ceil(a: Float): Float: 切り上げ(天井関数) 常に値をより大きな整数に丸めます。

    import kotlin.math.ceil
    
    fun main() {
        println("--- ceil (切り上げ) ---")
        println("ceil(3.1) = ${ceil(3.1)}")   // 4.0
        println("ceil(3.9) = ${ceil(3.9)}")   // 4.0
        println("ceil(3.0) = ${ceil(3.0)}")   // 3.0
        println("ceil(-3.1) = ${ceil(-3.1)}") // -3.0
        println("ceil(-3.9) = ${ceil(-3.9)}") // -3.0
    }
    

java.math.BigDecimalクラスの使用

金融計算や高い精度が要求される計算では、FloatDoubleの浮動小数点数の精度問題(0.1が正確に表現できないなど)を避けるために、java.math.BigDecimalを使用することが強く推奨されます。BigDecimalは任意の精度で10進数を表現でき、多様な丸めモード(RoundingMode)を提供します。

主なRoundingMode

  • UP: ゼロから遠ざかる方向へ丸める(kotlin.math.roundと似た挙動)。
  • DOWN: truncateと同じ。ゼロ方向へ丸める。
  • FLOOR: floorと同じ。負の無限大方向へ丸める。
  • CEILING: ceilと同じ。正の無限大方向へ丸める。
  • HALF_EVEN: 銀行型丸め、JIS丸めとも呼ばれます。0.5は最も近い偶数に丸めます。
  • HALF_DOWN: 0.5は常に切り捨て。
  • HALF_UP: いわゆる「四捨五入」。0.5は常に切り上げ。
import java.math.BigDecimal
import java.math.RoundingMode

fun main() {
    println("\n--- BigDecimal を使用した丸め ---")

    val num = BigDecimal("2.5")    // 文字列で初期化するのが最も安全
    val num2 = BigDecimal("2.6")
    val num3 = BigDecimal("3.5")
    val numNeg = BigDecimal("-2.5")

    // ROUNDING_MODE.HALF_UP (通常の四捨五入: 0.5は切り上げ)
    println("HALF_UP(2.5) = ${num.setScale(0, RoundingMode.HALF_UP)}")        // 3
    println("HALF_UP(2.6) = ${num2.setScale(0, RoundingMode.HALF_UP)}")       // 3
    println("HALF_UP(-2.5) = ${numNeg.setScale(0, RoundingMode.HALF_UP)}")    // -3

    // ROUNDING_MODE.HALF_DOWN (0.5は切り捨て)
    println("\nHALF_DOWN(2.5) = ${num.setScale(0, RoundingMode.HALF_DOWN)}")       // 2
    println("HALF_DOWN(-2.5) = ${numNeg.setScale(0, RoundingMode.HALF_DOWN)}")    // -2

    // ROUNDING_MODE.HALF_EVEN (銀行型丸め: 0.5は最も近い偶数へ)
    println("\nHALF_EVEN(2.5) = ${num.setScale(0, RoundingMode.HALF_EVEN)}")        // 2 (2は偶数)
    println("HALF_EVEN(3.5) = ${num3.setScale(0, RoundingMode.HALF_EVEN)}")        // 4 (4は偶数)
    println("HALF_EVEN(-2.5) = ${numNeg.setScale(0, RoundingMode.HALF_EVEN)}")    // -2

    // ROUNDING_MODE.CEILING (切り上げ)
    println("\nCEILING(2.1) = ${BigDecimal("2.1").setScale(0, RoundingMode.CEILING)}")   // 3
    println("CEILING(-2.1) = ${BigDecimal("-2.1").setScale(0, RoundingMode.CEILING)}") // -2

    // ROUNDING_MODE.FLOOR (切り捨て)
    println("\nFLOOR(2.1) = ${BigDecimal("2.1").setScale(0, RoundingMode.FLOOR)}")     // 2
    println("FLOOR(-2.1) = ${BigDecimal("-2.1").setScale(0, RoundingMode.FLOOR)}")   // -3
}

解説

  • setScale(スケール, 丸めモード)メソッドを使用して丸めを行います。スケールは小数点以下の桁数、丸めモードRoundingMode列挙型から選択します。
  • BigDecimalのインスタンスは、浮動小数点数から直接ではなく、文字列から初期化するのが最も安全です(例: BigDecimal("2.5"))。

文字列フォーマットによる丸め(表示用)

数値を計算のためではなく、表示目的で特定の小数点以下桁数に丸めて文字列として出力したい場合は、String.format()関数やKotlinの文字列テンプレートを使用できます。ただし、これは厳密な数値計算の丸めとは異なり、表示形式の調整です。

fun main() {
    println("\n--- 文字列フォーマットによる丸め (表示用) ---")

    val value = 12.34567

    // 小数点以下2桁に丸めて表示 (一般的な四捨五入)
    val formattedString1 = "%.2f".format(value)
    println("format(\"%.2f\"): $value -> $formattedString1") // 12.35

    // 小数点以下0桁に丸めて表示
    val formattedString2 = "%.0f".format(value)
    println("format(\"%.0f\"): $value -> $formattedString2") // 12

    val valueHalf = 12.5
    val formattedStringHalf = "%.0f".format(valueHalf)
    println("format(\"%.0f\"): $valueHalf -> $formattedStringHalf") // 13 (デフォルトで四捨五入に似た挙動)

    val negValueHalf = -12.5
    val formattedStringNegHalf = "%.0f".format(negValueHalf)
    println("format(\"%.0f\"): $negValueHalf -> $formattedStringNegHalf") // -12 (JavaのDecimalFormatのデフォルトはHALF_EVENに近い)

    // Kotlinの文字列テンプレートを使ったDoubleのtoString()
    // これも浮動小数点数の丸めルールに従う
    println("Double.toString() (12.3): ${12.3.toString()}")   // 12.3
    println("Double.toString() (12.0): ${12.0.toString()}")   // 12.0
}

解説

  • "%.nf".format(value)は、valueを小数点以下n桁に丸めて文字列としてフォーマットします。この丸めは通常、HALF_UP(四捨五入)に似た挙動を示します。ただし、内部的にはDouble.toString()DecimalFormatのデフォルトの丸めモードに依存するため、厳密な制御が必要な場合はBigDecimalを使用すべきです。

kotlin.math.roundは最も近い整数への丸め(.5はゼロから遠ざかる)を行うための基本的な関数ですが、要件に応じて以下の代替手法を検討してください。

  • 表示目的の丸め: String.format()
  • 厳密な精度と多様な丸めモード: java.math.BigDecimal
  • 異なる丸め規則: ceil(切り上げ)、floor(切り捨て)、truncate(ゼロ方向への切り捨て)。