Question

Kotlin String Formatting: Format Numbers in String Templates

kotlinstring-formatting

Question

Kotlin has a useful feature called string templates:

val i = 10
val s = "i = $i" // evaluates to "i = 10"

Can string templates also apply formatting?

For example, how can a Double be formatted inside a Kotlin string template so that it shows a fixed number of digits after the decimal point?

val pi = 3.14159265358979323
val s = "pi = $pi" // How can this become "pi = 3.14"?

Short Answer

By the end of this page, you will understand the difference between string templates and string formatting in Kotlin, how to format numbers like Double values, and the most common ways developers produce output such as 3.14 instead of a long decimal value.

Concept

Kotlin string templates let you insert values directly into strings using $variable or ${expression}. They are great for readability and convenience:

val name = "Ada"
println("Hello, $name")

However, string templates do not provide built-in formatting syntax like “show 2 decimal places” directly inside $value.

That means this does not exist as template-specific syntax:

// Not valid Kotlin formatting syntax
val s = "pi = $pi:2f"

Instead, you usually do one of these:

  1. Format the value first, then place it in the template.
  2. Use an expression inside ${...} that returns a formatted string.
  3. Use formatting functions such as String.format().

For numeric formatting, Kotlin commonly relies on Java's formatting tools on the JVM, such as:

val pi = 3.141592653589793
val s = "pi = ${"%.2f".format(pi)}"

This produces:

pi = 3.14

Why this matters:

  • Raw numbers may show too many decimal places.
  • User-facing output often needs clean formatting.
  • Reports, prices, measurements, and UI text usually require controlled precision.

So the key idea is:

  • String templates insert values
  • Formatting functions shape values before insertion

Mental Model

Think of a string template as a label printer.

  • $pi means: “print whatever is currently in pi.”
  • It does not mean: “decide how many decimal places to show.”

If you want prettier output, you must prepare the value before printing the label.

So instead of this:

  • “Print the raw number”

You do this:

  • “First convert the number into a nicely formatted string”
  • “Then insert that string into the template”

In short:

  • Template = insertion
  • Format = conversion before insertion
Take Quiz

Syntax and Examples

Basic string template

val pi = 3.141592653589793
println("pi = $pi")

Output:

pi = 3.141592653589793

Format to 2 decimal places

A common Kotlin approach is:

val pi = 3.141592653589793
val s = "pi = ${"%.2f".format(pi)}"
println(s)

Output:

pi = 3.14

What "%.2f" means

  • %f = floating-point number
  • .2 = show 2 digits after the decimal point
  • %.2f = format as a decimal with 2 digits after the decimal

Using String.format

val pi = 3.141592653589793
val s = String.format("pi = %.2f", pi)
println(s)

Output:

pi = 3.14

Formatting inside a template expression

val price = 12.5
println("Price: ${"%.2f".format(price)}")

Output:

Price: 12.50

Formatting more than one value

val width = 5.6789
val height = 9.1234
println("Size: ${"%.1f".format(width)} x ${"%.1f".format(height)}")

Output:

Size: 5.7 x 9.1

Step by Step Execution

Consider this example:

val pi = 3.141592653589793
val formatted = "%.2f".format(pi)
val result = "pi = $formatted"
println(result)

Step by step:

  1. val pi = 3.141592653589793

    • A Double value is stored in pi.
  2. val formatted = "%.2f".format(pi)

    • The format pattern %.2f is applied to pi.
    • The result is the string "3.14".
  3. val result = "pi = $formatted"

    • Kotlin inserts the already formatted string into the template.
    • result becomes "pi = 3.14".
  4. println(result)

    • The final text is printed.

Final output:

pi = 3.14

You can also do it in one line:

println("pi = ${"%.2f".format(pi)}")

That works because ${...} allows any expression that returns a value.

Real World Use Cases

Formatting numbers inside strings is common in real programs.

Prices and money

val total = 19.9
println("Total: ${"%.2f".format(total)}")

Useful for receipts, checkout pages, and invoices.

Measurements

val temperature = 21.678
println("Temperature: ${"%.1f".format(temperature)}°C")

Useful in dashboards, sensor apps, and weather displays.

Reports and analytics

val conversionRate = 0.45678
println("Conversion rate: ${"%.2f".format(conversionRate * 100)}%")

Useful for logs, summaries, and business reports.

Scientific or engineering output

val voltage = 3.14159
println("Voltage: ${"%.3f".format(voltage)} V")

Useful when controlled precision matters.

Logging readable values

val loadTime = 1.234567
println("Load time: ${"%.2f".format(loadTime)} seconds")

Useful when raw floating-point output is too noisy.

Take Quiz

Real Codebase Usage

In real projects, developers rarely sprinkle raw numeric values directly into user-facing strings. Instead, they usually format values close to the presentation layer.

Common patterns

Format before display

fun formatPrice(price: Double): String {
    return "%.2f".format(price)
}

Then use it:

println("Total: ${formatPrice(12.5)}")

This keeps formatting logic reusable.

Guard clauses before formatting

fun formatScore(score: Double?): String {
    if (score == null) return "N/A"
    return "%.1f".format(score)
}

Useful when values may be missing.

Validation before output

fun formatPercentage(value: Double): String {
    require(value >= 0) { "Percentage cannot be negative" }
    return "%.2f%%".format(value)
}

This prevents invalid display values.

Centralized formatting helpers

In larger codebases, formatting is often placed in helper functions or formatter classes so the app uses consistent decimal places everywhere.

object NumberFormatter {
    fun twoDecimals(value: Double): String = "%.2f".format(value)
}

Early return for special cases

fun formatDistance(distance: Double): String {
    if (distance == 0.0) return "0 m"
    return "%.2f m".format(distance)
}

This makes edge cases explicit and easy to read.

Common Mistakes

1. Expecting formatting syntax inside $value

Broken idea:

val pi = 3.14159
val s = "pi = $pi%.2f"

Why it is wrong:

  • Kotlin templates do not understand formatting directives attached to $pi.

Correct approach:

val s = "pi = ${"%.2f".format(pi)}"

2. Forgetting ${...} for expressions

Broken code:

val pi = 3.14159
val s = "pi = "%.2f".format(pi)

Why it is wrong:

  • Inside a string template, complex expressions must go inside ${...}.

Correct code:

val s = "pi = ${"%.2f".format(pi)}"

3. Confusing rounding with truncation

val pi = 3.14159
println("%.2f".format(pi))

This gives:

3.14

Formatting usually rounds the number to the requested precision. It does not simply cut off digits in all cases.

4. Using raw Double output for UI text

val total = 12.0
println("Total: $total")

This may display 12.0 when you want 12.00.

Better:

println("Total: ${"%.2f".format(total)}")

5. Mixing locale-sensitive output without thinking

Depending on environment and locale, decimal separators can vary when formatting. In some contexts, you may need to specify a locale explicitly.

Example:

val s = java.lang.String.format(java.util.Locale.US, "%.2f", 3.14)

Use this when output must always use . as the decimal separator.

Comparisons

ApproachWhat it doesBest forExample
$valueInserts raw valueQuick debugging or simple strings"pi = $pi"
${expression}Inserts result of an expressionFormatting or calculations inside templates"pi = ${"%.2f".format(pi)}"
"%.2f".format(value)Formats one value into a stringSimple numeric formatting"%.2f".format(pi)
String.format(...)Formats a whole string with placeholdersMultiple formatted values in one callString.format("pi = %.2f", pi)

String templates vs string formatting

FeatureString templateString formatting
PurposeInsert values into stringsControl how values appear
Syntax$name, ${expression}"%.2f".format(value)
Handles decimal places directlyNoYes
Good for readabilityYesYes, especially for numeric output

When to use which

  • Use string templates when you just need to insert values.
  • Use formatting when appearance matters.
  • Combine both when you want readable code:
println("pi = ${"%.2f".format(pi)}")
Take Quiz

Cheat Sheet

Quick reference

Insert a raw value

val x = 10
println("x = $x")

Insert an expression

val x = 10
println("double = ${x * 2}")

Format a Double to 2 decimal places

val pi = 3.14159
println("%.2f".format(pi))

Format inside a string template

println("pi = ${"%.2f".format(pi)}")

Format a whole string

val s = String.format("pi = %.2f", pi)

Useful patterns

  • %.1f → 1 digit after decimal
  • %.2f → 2 digits after decimal
  • %.3f → 3 digits after decimal
  • %d → integer
  • %s → string

Rules to remember

  • $variable inserts the value as-is.
  • ${...} is required for expressions.
  • String templates do not have built-in decimal formatting syntax.
  • Use .format(...) or String.format(...) for formatted output.

Common answer to the original problem

val pi = 3.141592653589793
val s = "pi = ${"%.2f".format(pi)}"

FAQ

How do I format a Double to 2 decimal places in Kotlin?

Use:

"%.2f".format(value)

Example:

val pi = 3.14159
println("%.2f".format(pi)) // 3.14

Can Kotlin string templates format numbers directly?

No. String templates insert values, but they do not provide built-in numeric formatting rules like decimal precision.

What is the difference between $value and ${expression} in Kotlin?

  • $value inserts a variable directly.
  • ${expression} evaluates an expression and inserts its result.

How do I format a number inside a Kotlin string template?

Use a formatting expression inside ${...}:

println("pi = ${"%.2f".format(pi)}")

Does %.2f round the number?

Yes, it formats the number to 2 decimal places using rounding.

Should I use String.format() or .format() in Kotlin?

Both are useful. .format() is concise for single values, while String.format() can be clearer when formatting a full string with multiple placeholders.

Why does my formatted number sometimes use a comma instead of a dot?

Formatting can be locale-sensitive. If you need a specific decimal separator, specify a locale explicitly.

Take Quiz

Mini Project

Description

Build a small Kotlin program that prints a mini receipt. The goal is to practice formatting Double values for display inside strings, which is a common task in command-line apps, reports, and UI text.

Goal

Create a receipt that displays item prices and a final total using exactly 2 decimal places.

Requirements

  • Define at least three item prices as Double values.
  • Print each item price using exactly 2 decimal places.
  • Calculate the total price.
  • Print the total using a string template with formatted output.
Take Quiz

Keep learning

Related questions

View more