Repository: JetBrains/kotlin
File: kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/FloatingPointParser.kt (Kotlin)

Code before:
            ++start
            --length
            negative = true
        } else if (c == '+') {
            ++start
            --length
        }
        if (length == 0)
            throw NumberFormatException(s)

        decimal = s.indexOf('.')
        if (decimal > -1) {
            shift = end - decimal - 1
            // Prevent e overflow, shift >= 0.
            if (e >= 0 || e - Int.MIN_VALUE > shift) {
                e -= shift
            }
            s = s.substring(start, decimal) + s.substring(decimal + 1, end)
        } else {
            s = s.substring(start, end)
        }

        length = s.length
        if (length == 0)
            throw NumberFormatException()

        end = length
        while (end > 1 && s[end - 1] == '0')
            --end

        start = 0
        while (start < end - 1 && s[start] == '0')
            start++

        if (end != length || start != 0) {
            shift = length - end
            if (e <= 0 || Int.MAX_VALUE - e > shift) {
                e += shift
            }
            s = s.substring(start, end)
        }

        // Trim the length of very small numbers, natives can only handle down to E-309.
        val APPROX_MIN_MAGNITUDE = -359
        val MAX_DIGITS = 52
        length = s.length
        if (length > MAX_DIGITS && e < APPROX_MIN_MAGNITUDE) {
            val d = minOf(APPROX_MIN_MAGNITUDE - e, length - 1)
            s = s.substring(0, length - d)
            e += d
        }

Review comment (performance):
@ilmat192 I would also like to refactor this method in the future, as I can see that it can lead to performance degradation in the parsing of strings. Do you have any benchmarks or environments where I can check my fixes?

I do not want to break anything in this PR, so I decided not to refactor this part of the complex logic, but I would definitely like to.

Code after:
        val result = parserImpl(info.s, info.e)
        return if (info.negative) unaryMinus(result) else result
    }

    /**
     * To unify the logic of Double and Float parsing we need a common method to calculate a negative value.
     * Unfortunately in this case we need to make uncheck cast to generic type T to have a proper return type.
     */
    @Suppress("UNCHECKED_CAST")
    private inline fun <reified T> unaryMinus(value: T): T {
        return when (value) {
            is Float -> -value as T
            is Double -> -value as T
            else -> throw NumberFormatException()
        }
    }

    /**
     * Takes a String and does some initial parsing. Should return a
     * StringExponentPair containing a String with no leading or trailing white
     * space and trailing zeroes eliminated. The exponent of the
     * StringExponentPair will be used to calculate the floating point number by
     * taking the positive integer the String represents and multiplying by 10
     * raised to the power of the exponent.
     *
     * This method is not optimal, can cause performance issues. This logic can be done in one single run.
     * It also violates Kotlin coding practices and should be refactored in the future after stabilizing the logic.
     *
     * @param string the String that will be parsed to a floating point
     * @return a StringExponentPair with necessary values
     * @exception NumberFormatException if the String doesn't pass basic tests
     */
    private fun initialParse(string: String): StringExponentPair {
        var s = string
        var length = s.length
        var negative = false
        val decimal: Int
        var shift: Int
        var e = 0
        var start = 0
        var c: Char = s[length - 1]

        // Checking that the initial string ends with one of valid prefixes(D/d/F/f) and skipping it.
        if (c == 'D' || c == 'd' || c == 'F' || c == 'f') {
            length--
            if (length == 0)
                throw NumberFormatException(s)
        }

        // Getting exponent separator from the string (E/e)
        var end = maxOf(s.indexOf('E'), s.indexOf('e'))