java / kotlin: формат double / int с фиксированным количеством цифр

#java #kotlin #format #bigdecimal #decimalformat

#java #kotlin #формат #bigdecimal #десятичный формат

Вопрос:

Я попытался поискать, но у меня возникли проблемы с поиском полного решения этой проблемы: я хочу отформатировать an Int так, чтобы общее количество цифр всегда было 3. Несколько примеров:

 1000000 -> 1,00m
678945 -> 678k
65432 -> 65,4k
5437 -> 5,43k
 

числа никогда не должны быть меньше 1000 или больше 1 миллиарда, поэтому другие случаи не имеют большого значения

это самое близкое, к чему я пришел:

 @JvmStatic
        fun formatPointsTop3(points: Int?): String {
            if (points == null) return ""

            val formatter = NumberFormat.getInstance(Locale.ITALIAN)
            val mathContext = MathContext(3, RoundingMode.DOWN)


            return when {
                points < 1000 -> {
                    "$points"
                }
                points < 1000000 -> {
                    val bigDecimal = BigDecimal(points / 1000.0, mathContext)
                    "${formatter.format(bigDecimal)}k"
                }
                else -> {
                    val bigDecimal = BigDecimal(points / 1000000.0, mathContext)
                    "${formatter.format(bigDecimal)}m"
                }
            }
        }
 

это работает нормально для большинства случаев, за исключением 1000000, который выводит 1 м (я хочу 1,00м). Добавление formatter.minimumFractionDigits = 2 добавляет десятичные дроби ко всем числам.

Есть ли способ добиться этого, не делая странных вещей непосредственно в строке?

Ответ №1:

Должно быть. Во-первых, выясните, как вы хотите его отобразить, что, похоже, у вас другое представление о том, как именно per log(n) (например, в зависимости от количества цифр во входных данных). Для 7 цифр вы хотите отобразить значение, разделенное на миллион, с 2 дробными цифрами. Для 8 цифр требуется значение div million с 1 дробью и т.д. Это будет много кода, возможно, манипулирование строками проще.

Ответ №2:

В итоге я сделал это:

 fun formatPointsTop3(points: Int?): String {
            if (points == null) return ""
            if (points == 0) return "0"

            val maxNumberOfDigits = 3

            val multiplierExponent = log(points.toDouble(), 1000.0).toInt()

            val bigPoints = BigDecimal(points)
            val bigDivider = BigDecimal(1000).pow(multiplierExponent)
            val reducedPoints = bigPoints.divide(bigDivider)

            val intPart = reducedPoints.toBigInteger().toInt()
            val decimalPart = reducedPoints.subtract(BigDecimal(intPart)).toDouble()

            val numberOfDecimals = maxNumberOfDigits - (log10(intPart.toDouble()).toInt()   1)

            var formattedString = "$intPart"

            if (numberOfDecimals > 0) {
                val multiplier = 10.0.pow(numberOfDecimals)
                val formatter = NumberFormat.getIntegerInstance()
                formatter.minimumIntegerDigits = numberOfDecimals
                formatter.maximumIntegerDigits = numberOfDecimals
                val multiplied = (decimalPart * multiplier).toInt()
                formattedString  = ",${formatter.format(multiplied)}"
            }

            formattedString  = when (multiplierExponent) {
                0 -> ""
                1 -> "k"
                2 -> "m"
                3 -> "b"
                else -> ""  //this is not possible as max int is 2 billions
            }

            return formattedString
        }
 

это довольно некрасиво, но выглядит лучше, чем манипулирование строками, imo