How to convert a decimal number to a rational number in Kotlin

1 Answer

0 votes
import java.math.BigInteger

/*
    DecimalToRational (Kotlin)
    --------------------------
    Converts a decimal number (given as a string) into an exact rational p/q.

    Why parse the string manually?
        • Kotlin/Java have no built‑in rational type.
        • Double cannot preserve exact decimal digits.
        • Using strings + BigInteger ensures perfect accuracy.

    Algorithm:
        1. Look for a decimal point.
        2. If none → integer → numerator = n, denominator = 1.
        3. Otherwise:
              Example: "12.345"
              integer part   = 12
              fractional part = 345
              digits = 3

              numerator   = integer_part * 10^digits + fractional_part
              denominator = 10^digits

        4. Reduce using gcd (Euclid’s algorithm).
*/

data class Rational(val numerator: BigInteger, val denominator: BigInteger) {
    override fun toString(): String = "$numerator/$denominator"
}

// Euclid’s GCD algorithm
fun gcd(a: BigInteger, b: BigInteger): BigInteger {
    var x = a
    var y = b
    while (y != BigInteger.ZERO) {
        val t = y
        y = x % y
        x = t
    }
    return x.abs()
}

// Convert decimal string to Rational
fun convertDecimalToRational(s: String): Rational {
    val dotPos: Int = s.indexOf('.')

    if (dotPos == -1) {
        // No decimal point → integer
        return Rational(BigInteger(s), BigInteger.ONE)
    }

    // Split into integer and fractional parts
    val intPart: String = s.substring(0, dotPos)
    val fracPart: String = s.substring(dotPos + 1)

    val integerValue: BigInteger = BigInteger(intPart)
    val fractionalValue: BigInteger = BigInteger(fracPart)

    val digits: Int = fracPart.length

    // denominator = 10^digits
    val denominator: BigInteger = BigInteger.TEN.pow(digits)

    // numerator = integerValue * denominator + fractionalValue
    val numerator: BigInteger = integerValue * denominator + fractionalValue

    // Reduce using gcd
    val g: BigInteger = gcd(numerator, denominator)

    return Rational(numerator / g, denominator / g)
}

fun main() {
    val values: List<String> = listOf(
        "3.5", "12.75", "0.125", "100.001",
        "7", "42.0", "0.333", "5.2"
    )

    for (v in values) {
        val r: Rational = convertDecimalToRational(v)
        println("$v -> $r")
    }
}



/*
run:

3.5 -> 7/2
12.75 -> 51/4
0.125 -> 1/8
100.001 -> 100001/1000
7 -> 7/1
42.0 -> 42/1
0.333 -> 333/1000
5.2 -> 26/5

*/

 



answered Jul 23 by avibootz
...