How to Format Floats Within F-Strings in Python

How to Format Floats Within F-Strings in Python

by Ian Eyre Apr 17, 2024 basics python

You’ll often need to format and round a Python float to display the results of your calculations neatly within strings. In earlier versions of Python, this was a messy thing to do because you needed to round your numbers first and then use either string concatenation or the old string formatting technique to do this for you.

Since Python 3.6, the literal string interpolation, more commonly known as a formatted string literal or f-string, allows you to customize the content of your strings in a more readable way.

An f-string is a literal string prefixed with a lowercase or uppercase letter f and contains zero or more replacement fields enclosed within a pair of curly braces {...}. Each field contains an expression that produces a value. You can calculate the field’s content, but you can also use function calls or even variables.

While most strings have a constant value, f-strings are evaluated at runtime. This makes it possible for you to pass different data into the replacement fields of the same f-string and produce different output. This extensibility of f-strings makes them a great way to embed dynamic content neatly inside strings. However, even though f-strings have largely replaced the earlier methods, they do have their short-comings.

For example, one of the most common attacks performed on a relational database is a SQL injection attack. Often, users provide parameters to SQL queries, and if the query is formed within an f-string, it may be possible to damage a database by passing in rogue commands. F-strings can also be used in a denial-of-service attack by attacking Python’s logging module code.

In older versions of Python, f-strings had a number of other limitations that were only fixed with Python version 3.12. This version is used throughout this tutorial.

Take a look at the example below. It shows you how to embed the result of a calculation within an f-string:

Python
>>> f"One third, expressed as a float is: {1 / 3}"
'One third, expressed as a float is: 0.3333333333333333'

Without any explicit rounding, once an expression has produced its value it’ll be inserted into the string using a default number of decimal places. Here, the result is shown to sixteen decimal places, which, in most cases, is more precision than you’ll ever need. So you’ll likely want to round your final answer to a more practical number of digits.

In this tutorial, you’ll learn how to use a Python f-string to format floats to meet your requirements.

Take the Quiz: Test your knowledge with our interactive “Format Floats Within F-Strings” quiz. You’ll receive a score upon completion to help you track your learning progress:


Interactive Quiz

Format Floats Within F-Strings

In this quiz, you'll test your understanding of how to format floats within f-strings in Python. This knowledge will let you control the precision and appearance of floating-point numbers when you incorporate them into formatted strings.

How to Format and Round a Float Within a Python F-String

To format a float for neat display within a Python f-string, you can use a format specifier. In its most basic form, this allows you to define the precision, or number of decimal places, the float will be displayed with.

The code below displays the same calculation as before, only it’s displayed more neatly:

Python
>>> f"One third, rounded to two decimal places is: {1 / 3:.2f}"
'One third, rounded to two decimal places is: 0.33'

To use Python’s format specifiers in a replacement field, you separate them from the expression with a colon (:). As you can see, your float has been rounded to two decimal places. You achieved this by adding the format specifier .2f into the replacement field. The 2 is the precision, while the lowercase f is an example of a presentation type. You’ll see more of these later.

Python’s f-strings also have their own mini-language that allows you to format your output in a variety of different ways. Although this tutorial will focus on rounding, this is certainly not the only thing you can use them for. As you’ll see later, their mini-language is also used in other string formatting techniques.

In addition to displaying the result of calculations, the precision part of a format specifier can also be applied directly to variables and the return values of function calls:

Python
>>> def total_price(cost):
...     return cost * 1.2
...

>>> cost_price = 1000
>>> tax = 0.2
>>> f{1000:,.2f} + £{cost_price * tax:,.2f} = £{total_price(cost_price):,.2f}"
'£1,000.00 + £200.00 = £1,200.00'

This time, you’ve used multiple replacement fields in the same string. The first one formats a literal number, the second formats the result of a calculation, while the third formats the return value from a function call. Also, by inserting a comma (,) before the decimal point (.) in the format specifier, you add a thousands separator to your final output.

In everyday use, you display numbers with a fixed amount of decimals, but when performing scientific or engineering calculations, you may prefer to format them using significant figures. Your results are then assumed to be accurate to the number of significant figures you display them with.

If you want to round numbers to significant figures, you use the lowercase letter g in the format specifier. You can also use an uppercase G, but this automatically switches the format to scientific notation for large numbers.

Suppose you have a circle with a radius of 10.203 meters. To work out the area, you could use this code:

Python
>>> import math

>>> f"Area = {math.pi * 10.203**2}"
'Area = 327.0435934242156'

You decide to use the value of the pi constant provided by Python’s math module. While your answer certainly looks highly precise, much of it is meaningless. Any calculation is only as precise as the lowest number of significant figures in any of its components. In this case, the radius value of 10.203 contains only five significant figures, which is less than math.pi, so that’s what the final result should be rounded to.

A better way of coding this calculation would be:

Python
>>> f"Area = {math.pi * 10.203**2:,.5g}"
'Area = 327.04'

The area, correct to five significant figures, is 327.04 square meters.

You can also use format specifiers to display numbers as percentages. To do this manually, you need to multiply the number by one hundred and append it with a percent sign (%) before displaying it with the required amount of decimal places. You can do all of this automatically by using the % presentation type in place of the lowercase f or g:

Python
>>> f"{0.1256:.1%}"
'12.6%'
>>> f"{0.5:.2%}"
'50.00%'
>>> f"{3:.2%}"
'300.00%'

As you can see, the .2% has taken care of the multiplication, rounding, and % appendage for you automatically.

You now know how to deal with the most common scenarios where you’ll need to format floats within an f-string in Python. You’ll learn more in the next section, but before moving on, expand the section below and see if you can complete the tasks. Working through these is a great way to review and consolidate your understanding so far.

Task 1: Using number = 64723.4161, see if you can write a single line of code to produce the output shown. Apart from those needed by the format specifier, your string should contain no other numbers:

Python
>>> number = 64723.4161
>>> # Write your solution here
'The number 64,723.4161, when rounded to two decimal places, is 64_723.42.'

Task 2: Start with the variables numerator = 3 and denominator = 8, and see if you can write a single line of code to produce the output shown. Again, your string should contain no other numbers apart from those needed by the format specifier:

Python
>>> numerator = 3
>>> denominator = 8
>>> # Write your solution here
'3/8 as a percentage is 37.5%.'

Task 3: Calculate the volume of a sphere whose radius is 2.340 meters. Your answer should be displayed to an appropriate number of significant figures.

You’ll find possible solutions in the downloadable materials that accompany this tutorial.

Now, it’s time to move on and learn about more ways you can enhance the display of your numbers within strings.

Customizing the Width of Your Formatted Strings

In addition to their precision, format specifiers contain a width parameter. This allows you to set the total number of characters used to display the number. If your number’s length exceeds the width parameter, then additional space is assigned. Otherwise, the result will be left-padded with spaces to its specified width. Assuming you set your width to fit the largest number, each number will have the same fixed length as the others.

Fixed-length records are useful for saving to a file. A fixed-length file is one whose record fields all occupy the same number of characters. Usually, fields are padded with additional characters, most commonly trailing spaces for text and leading zeros for numbers.

Fixed-length files have been around for years, and you still see them today. Although you’ll find formats like XML and YAML easier to read manually, fixed-length files do still offer some advantages. Your code can read fixed-length records very efficiently because each field within each record is in the same position. In contrast, XML parsers usually require more resources, particularly if you have large volumes of data to deal with.

However, fixed-length files are usually more application-specific than other formats, meaning you can’t transfer them easily between systems. Also, using a parser with XML and YAML allows you to quickly add and read additional fields. In contrast, updating fixed-length fields usually involves a major recoding of each program that needs to work with them.

To see the effect of the width parameter, take a look at the code shown below:

Python
>>> sample = 12345.6789

>>> print(
...     f"(1) |{sample:.2f}|",
...     f"(2) |{sample:1.2f}|",
...     f"(3) |{sample:8.2f}|",
...     f"(4) |{sample:12.2f}|",
...     sep="\n",
... )
(1) |12345.68|
(2) |12345.68|
(3) |12345.68|
(4) |    12345.68|

In each of these four results, the precision is set to .2f, meaning everything is rounded to two decimal places. Because you’re dealing with numbers, they’re left-padded with blanks by default. You used a newline character (\n) as a value separator to print each output in its own line. You also displayed each number between pipe characters (|) to emphasize the padding.

In the first result, no width was specified, so the f-string took care of it for you and sized the string to contain everything before the decimal point as well as the specified two digits after.

In the second example, the width is too small to accommodate the output, so it’s resized to allow the string to fit.

In the third example, the width is exactly the size required, so there’s no padding and again the same result occurs.

The final example shows padding has been applied to the output. The width is four characters beyond that required, so four blank spaces have been inserted into the start of the string.

Unless you need this additional padding, it’s generally better to ignore the width value completely and let the f-string take care of it for you.

You can also control the placement of the padding if you need to. It’s possible to left, right, and even center-align your data within the specified width:

Python
>>> sample = 12345.6789

>>> print(
...     f"(1) |{sample:<12,.2f}|",
...     f"(2) |{sample:>12,.2f}|",
...     f"(3) |{sample:^12,.2f}|",
...     sep="\n",
... )
(1) |12,345.68   |
(2) |   12,345.68|
(3) | 12,345.68  |

To control your padding placement, you use the less than (<), greater than (>), or caret (^) alignment options. These allow you to left, right, and center-align the number within the width value, respectively.

You may have noticed that only three space characters have been added, instead of the four you saw earlier. This is because by including the thousands separator in your format specifier, you have used up an additional character space.

Now suppose you want to use padding but would instead prefer a different padding character to the default space you’ve used so far. To do this, you add your chosen padding character before the alignment option:

Python
>>> sample = 12345.6789

>>> print(
...     f"(1) |{sample:*<12.2f}|",
...     f"(2) |{sample:*>12.2f}|",
...     f"(3) |{sample:*^12.2f}|",
...     sep="\n",
... )
(1) |12345.68****|
(2) |****12345.68|
(3) |**12345.68**|

In this example, you’ve chosen to pad your numbers out with an asterisk (*) character, although you could have used any other character. To do this, you added the asterisk into the format specifier. Your number was formatted to the expected two decimal places and padded out with an appropriate number of asterisks to make sure it was the 12 characters wide you wanted.

Controlling the Placement of Number Signs

In each of your examples so far, you’ve used positive numbers. Feel free to rerun any of them with negative numbers, and you’ll see that a negative sign will appear next to them. Positive numbers remain unsigned to adhere to the usual assumption that unsigned numbers are positive.

Suppose you want to display a sign against every number for emphasis or clarification, or to create fixed-length records for filing. To force the display of a sign against all of your numbers, you add the plus sign (+) option into the format specifier as shown:

Python
>>> sample = -12345.68

>>> print(
...     f"(1) |{sample:12,.2f}|",
...     f"(2) |{sample:+12,.2f}|",
...     f"(3) |{-sample:+12,.2f}|",
...     sep="\n",
... )
(1) |  -12,345.68|
(2) |  -12,345.68|
(3) |  +12,345.68|

This time, the number you used is negative. In the first example, when you use the 12,.2f format specifier, the negative sign appears. The second example gives you the same output, but you’ve added a plus sign to the start of the format specifer. The benefit of this only becomes apparent when you negate the number in example three. The plus sign (+) option in the format specifier now forces the display of the plus sign in your output.

You now know how to deal with some of the intricacies surrounding the display of rounded numbers. There are still some interesting things for you to learn, but before you move on, it’s time to put your thinking cap on again. Why not see if you can solve the following tasks?

Task 4a: Using sample = -12345.6789, see if you can write a single line of code to produce the output shown. The output should be left-aligned, and twelve characters wide with two decimal places of precision. Don’t forget the command separators and include all zeros:

Python
>>> sample = -12345.6789

>>> print(
...      # Write your solution here
... )
|-12,345.68  |
|+12,345.68  |

Task 4b: Update your previous output so it displays like that shown below. It’s very similar to the previous output except the plus sign (+) has been replaced with a space. Ideally, you should use a similar format specifier for both outputs. Remember to consult the Python documentation if you need help:

Python
>>> sample = -12345.6789

>>> print(
...      # Write your solution here
... )
|-12,345.68  |
| 12,345.68  |

Task 5: Start with result = 5 * 3, then see if you can produce the output shown without encoding a literal decimal point symbol (.) onto the end of the f-string. Instead, code the trailing decimal point using an appropriate format specifier syntax:

Python
>>> result = 5 * 3

>>> print(
...      # Write your solution here
... )
By default, the result is displayed like this: 15
However, some people love trailing decimal points like this: 15.

Task 6: Start with the three variables, customer_id = "C123456", amount1 = 12.34, and amount2 = 56.78, and see if you can produce the fixed-length record as shown:

Python
>>> customer_id = "C123456"
>>> amount1 = 12.34
>>> amount2 = 56.78

>>> print(
...      # Write your solution here
... )
C12345600012.3400056.78

The customer_id should be written as is, but amount1 and amount2 should be zero-padded to a total length of eight characters and two decimal places, as shown above.

You’ll find possible solutions in the downloadable materials that accompany this tutorial.

Now that you’ve learned how to round everyday numbers and have had a chance to practice your skills, it’s time to look at how to format both large and small numbers, as well as the impossible-to-imagine complex numbers.

Rounding Scientific Notation and Complex Numbers

When you want to write very large or very small numbers concisely, you often use scientific notation. Python, like many other programming languages, uses E notation to display scientific notation, as you can see below:

Numbers represented in mathematical scientific notation and Python scientific notation fomats.

In the figure above, you can see two examples of numbers formatted with scientific notation. The first uses the traditional mathematical representation, while the second uses Python’s representation.

You won’t be surprised to learn that you can display numbers with scientific notation using format specifiers. Here’s how you do it:

Python
>>> f"{1234.56789:.2e}"
'1.23e+03'

>>> f"{0.00012345:.3e}"
'1.234e-04'

A common specifier you might use to format numbers in scientific notation is the lowercase (e) presentation type. This uses a lowercase letter e in its display.

In the first example, because you set a precision of .2e, your string will display the number in scientific notation rounded to two decimal places. It’s also possible to add a width value. As before, this simply pads out the result to fit the width where necessary.

In the second example, you are going in the other direction and displaying a very small number. As a result, the scientific notation raises the power of ten to -04. This time, your use of .3e has displayed the result to three decimal places.

As with all numbers you pass through a format specifier, the format specifier is only a display tool. All calculations should use the original number to retain accuracy.

You can also use format specifiers with complex numbers. Complex numbers are widely used in engineering and physics to solve equations relating to oscillations. They can be used, for example, as part of a Fourier transform to remove noise frequencies from genuine transmissions.

A complex number is formed of both a real part and an imaginary part, with the imaginary part being a multiple of the square root of negative one. They’re normally written in the form a+bi, where a is called the real part, and b is called the imaginary part.

Despite the somewhat abstract nature of complex numbers, you’ll be glad to know that format specifiers have them covered:

Python
>>> value = 3.474 + 2.323j
>>> print(
...     f"The complex number {value} is formed",
...     f"from the real part {value.real:.2f},",
...     f"the imaginary part {value.imag:.1f},",
...     f"and is approximately {value:.0f}.",
...     sep="\n",
... )
The complex number (3.474+2.323j) is formed
from the real part 3.47,
the imaginary part 2.3,
and is approximately 3+2j.

As you can see, you access the real and imaginary components of your complex number within an f-string expression using their .real and .imag properties. Once you have these, you can format them in the same way you format any other number. You can also format the entire complex number at once by applying the format specifier directly to it.

In the above example, .2f formats the real part with two decimal places, then .1f formats the imaginary part to one decimal place. Finally, .0f formats the complete number to zero decimal places.

Soon, you’ll move away from the basic methods of rounding numbers with format specifiers in f-strings and learn how they can be used in other ways. Before you do, it’s time for another challenge.

Task 7: Using value = 13579+0.0245j, see if you can write a single line of code to produce the output shown below:

Python
>>> value = 13579+0.0245j
>>> print(
...     # Write your solution here
... )
The real part is 1.36E+04 and the imaginary part 2.45E-02.

See if you can work out the required format specifiers. Remember, you’re aiming for an uppercase E so you may need to read the documentation.

You’ll find possible solutions in the downloadable materials that accompany this tutorial.

Using decimal Objects to Mitigate Floating Point Inaccuracies

The Python float type limits you to 64 bits of storage for storing your numbers. This means that if any of your numbers exceed this size, they can’t be represented accurately. The code below shows an example:

Python
>>> f"{(10000000000000 / 3)}"
'3333333333333.3335'

The precise answer is an infinite number of recurring threes. As you can see, a trailing 5 has appeared because of space limitations.

You might think that using the format specifier .2f to display the output to two decimal places would solve the problem, but the inaccuracy is still there in the background. Format specifiers only round their data for display. They don’t truncate it.

For example, consider the following calculation:

Python
>>> f"{(10000000000000 / 3) + 0.6666}"
'3333333333334.0'

The result not only shows that the inaccuracy still exists but the error has now entered the integer part of your result. Further calculations would compound this issue and lead to greater inaccuracies in your final result.

Although the precision issues surrounding floating-point arithmetic on computers are an inherent part of their design, if you need accuracy assurance, then you should consider using the built-in decimal module instead. Decimal objects provide more control over floating-point arithmetic than the built-in float type because they allow you to perform calculations to a consistent precision and set that precision centrally.

To see this benefit in action, take a look at the code shown below:

Python
>>> from decimal import Decimal as D
>>> from decimal import getcontext

>>> getcontext().prec = 4
>>> f{float("0.1") + float("0.1") + float("0.1")}"
'£0.30000000000000004'

>>> f{D("0.1") + D("0.1") + D("0.1")}"
'£0.3'

The decimal module is part of Python’s standard library, so you don’t need to install it. However, to access the Decimal class from which you’ll instantiate objects, you must still import it. By importing Decimal with the letter D as its alias, you can use the alias when creating instances and, therefore, write more concise code.

You also import the getcontext() function. When you use the Decimal objects in code, many of their key properties are managed centrally by their context object. The getcontext() function obtains this for you. You then use its .prec attribute to set the precision of your Decimal objects. This defines the number of significant figures each Decimal will contain. Decimal instances are rounded to fit their precision.

Take a look at the output of the code above. When you use floats, the representation error appears. However, when Decimal instances are used, it’s nowhere to be seen. What’s more, the error won’t ever reappear either.

The takeaway here is that for very high precision, you should convert your strings to decimals rather than floats before you apply a format specifier. This prevents errors from filtering through to your calculations because anything that existed beyond the specified precision will have been safely removed. In addition, the decimals are more precise with a default precision of 28 places, while the floats have only about 16 places.

To help you decide which precision value to use, consider the concept of guarding figures. Suppose you are performing a calculation involving currency. Even though the result will probably contain two decimals, intermediate calculations should use four decimals to prevent rounding from affecting the final result. You should always use two guarding figures beyond the number of decimals you wish to display.

You can introduce guarding figures by using a combination of precision and format specifiers. Format specifiers can be applied to Decimal objects in the same way they can be applied to floats:

Python
>>> getcontext().prec = 4
>>> f{D("0.10001") + D("0.20001"):.2f}"
'£0.30'

To begin with, you are adding together two numbers which contain tiny inaccuracies in their original form. Because you have set the context precision to 4 significant figures, these will be removed when each string is converted into a Decimal object. Once the calculation proceeds, the format specifier rounds the displayed answer to two decimal places.

There are still a few other ways you can format numbers as strings in Python, which you’ll explore next.

Formatting Strings in Other Ways

Python’s built-in format() function is another way to produce formatted strings. The function takes two arguments: the value to be formatted plus any of the format specifiers you’ve already learned about. Some examples are shown below, with their f-string equivalents included for comparison:

Python
>>> format(-123.4567, "+9.2f")
'  -123.46'
>>> f"{-123.456:+9.2f}"
'  -123.46'

>>> format(0.125, ".2%")
'12.50%'
>>> f"{0.125:.2%}"
'12.50%'

As you can see, the format() function uses the same format specification mini-language as the more modern f-strings. Your first example uses +9.2f to display a string representing a signed float padded to 9 characters and rounded to 2 decimal places. You then use .2% to display the number to two decimal places as a percentage.

Take a moment to compare the code using format() to that using the f-string, and you’ll see that the f-string version is more compact and generally more readable.

One earlier string formatting method that still has a place today is the str.format() method. To use this, you need to insert replacement field placeholders into the string where you want your data to be. You then add format specifiers into the placeholders to specify how you want your data to appear. Finally, you pass the data to be inserted into the string as parameters to .format() in the same order as the placeholders.

The code example below illustrates all of this:

Python
>>> opposite = 1.234
>>> adjacent = 5.678
>>> hypotenuse = (opposite**2 + adjacent**2) ** 0.5
>>> template = "Opposite = {:0.1f}, Adjacent = {:0.2f}, Hypotenuse = {:0.3f}"
>>> template.format(opposite, adjacent, hypotenuse)
'Opposite = 1.2, Adjacent = 5.68, Hypotenuse = 5.811'

As you can see, the placeholders use each of the parameters passed to the .format() method in order. Although this isn’t quite as readable as the f-string alternative, you still use the same mini-language to define the formatting.

It is also possible to pass in values to .format() by keyword. When you add these keywords into the string’s replacement fields, the corresponding values will be inserted into the string when it’s displayed:

Python
>>> template = (
...     "Opposite = {opposite:0.1f}, "
...     "Adjacent = {adjacent:0.2f}, "
...     "Hypotenuse = {hypotenuse:0.3f}"
... )
>>> template.format(
...     hypotenuse=(1.234**2 + 5.678**2)**0.5,
...     adjacent=5.678,
...     opposite=1.234
... )
'Opposite = 1.2, Adjacent = 5.68, Hypotenuse = 5.811'

In this version, you pass parameters to .format() by keyword. Notice that the order of the parameters you pass doesn’t matter. As long as the keywords match the placeholders within the curly braces in your format specifiers, Python substitutes them accordingly.

Instead of passing the individual values as keyword parameters, you can follow a common pattern that involves a Python dictionary:

Python
>>> data = {
...     "opposite": 1.234,
...     "adjacent": 5.678,
...     "hypotenuse": (1.234**2 + 5.678**2) ** 0.5,
... }

>>> template = (
...     "Opposite = {opposite:0.1f}, "
...     "Adjacent = {adjacent:0.2f}, "
...     "Hypotenuse = {hypotenuse:0.3f}"
... )

>>> template.format(**data)
'Opposite = 1.2, Adjacent = 5.68, Hypotenuse = 5.811'

In your code, you’ve used the unpacking operator (**) to pass the dictionary data into .format(). This passes each of your dictionary keys as a separate named parameter and each of its values as the named parameter’s value.

As you can see, this code is edging closer to the syntax you use with f-strings. You could take this to the next level and create your own number formatting template:

Python
>>> num = "{:{align}{width}.{precision}f}"
>>> print(
...     num.format(
...         123.236, align="<", width=8, precision=2
...     )
... )
123.24

This time, you construct the format specifier by passing in your number to be formatted along with a set of named parameters to .format(). These get applied to your num template, which is then used to format your number.

When you use .format(), it’s also possible to define the format specifier dynamically at runtime. Suppose you have several words that you want to pad out to a width of four characters greater than their length. The code below shows you how to achieve this:

Python
>>> text = "Python is cool"
>>> padded_words = [
...     "{word:*^{length}}".format(word=word, length=len(word) + 4)
...     for word in text.split()
... ]

>>> padded_words
['**Python**', '**is**', '**cool**']

Once more, you construct the format specifier to include the name of the parameters that you’ll receive from .format(). The word to be formatted is assigned to word, while the dynamically calculated width is assigned to length. You achieve this by nesting the precision replacement field {length} within the format specifier. The asterisk (*) defines the padding character to be used, while the caret (^) symbol ensures the output is centered.

The padded_words list is created using a list comprehension. The .split() method is called on the content of the text variable, which separates out the original string by its space characters and produces a list containing its three words. The for loop then iterates over each of these and passes them to .format().

When each word is received by .format(), its value is assigned to the variable word, and its length plus four characters is assigned to the variable length. These variables are then applied to the format specifier to produce the formatted version in padded_words.

Pick up your thinking cap again. It’s time for more challenges.

Task 8: In the previous example, you produced this list of strings ['**Python**', '**is**', '**cool**'] with code based around the str.format() method. See if you can rewrite this using the f-string syntax.

Task 9: Start with the numbers = [1234.56789, 123.456789, 12.3456789, 1.23456789] list, and see if you can write a format specifier to produce a second list named numbers_formatted that contains ["1,234.5679", "123.457", "12.35", "1.2"].

As you can see, each number is rounded so that there are the same number of digits before the decimal point as there are after it.

You’ll find possible solutions in the downloadable materials that accompany this tutorial.

You have certainly worked hard in this tutorial. Now it’s time for a holiday.

Formatting Numbers for International Use

If ever you need to write code that will be used internationally, you need to make sure that numbers are formatted using the locale of the country they are running within. This will ensure your numbers and dates are formatted according to local conventions so that they are correctly understood. To support internationalization within your code, you can use Python’s locale module.

The locale module allows you to specify standard formats such as the thousands separator, currency symbol, and so on. You don’t usually need to set this explicitly on your computer because your operating system settings are used by default. However, if you want to be sure your code runs using a specific locale, you can set it explicitly.

You can find the locale your computer is currently using with the locale.getlocale() function:

Python
>>> import locale
>>> locale.getlocale()
('English_United Kingdom', '1252')

The computer used to run this code is using the English_United Kingdom locale. The 1252 is the character encoding used by Windows when encoding each English character. Each symbol occupies a single byte in memory. Run the code yourself to see your locale results.

If you want to format numbers according to the locale on your computer, use the n presentation type in your format specifier:

Python
>>> f"{-1234.567:n}"
'-1234.57'
>>> f"{-1234.567:.2f}"
'-1234.57'

As you can see, the n format specifier on a computer using the locale ('English_United Kingdom', '1252') is equivalent to using .2f as the format specifier. This locale dictates that the period (.) is used as the decimal point while the negative sign is placed to the left of the number.

To really appreciate what n does, you need to run it across a range of locales. Fortunately, the locale module provides a setlocale() function that allows you to do this. As long as your operating system supports the locales you’re interested in, you can change how numbers are displayed:

Python
>>> sample_locales = [
...     ("USA", "en_US.UTF-8"),
...     ("Poland", "pl_PL.UTF-8"),
...     ("UK", "en_GB.UTF-8"),
...     ("Czech Republic", "cs_CZ.UTF-8"),
...     ("Korea", "ko_KR.UTF-8"),
...     ("Germany", "de_DE.UTF-8"),
...     ("France", "fr_FR.UTF-8"),
... ]

>>> for name, loc in sample_locales:
...     _ = locale.setlocale(category=locale.LC_ALL, locale=loc)
...     print(
...         f"{name} uses",
...         f"{1234.567:n} and",
...         f"{-1234.567:n}",
...     )
...
USA uses 1,234.57 and -1,234.57
Poland uses 1 234,57 and -1 234,57
UK uses 1,234.57 and -1,234.57
Czech Republic uses 1 234,57 and -1 234,57
Korea uses 1,234.57 and -1,234.57
Germany uses 1.234,57 and -1.234,57
France uses 1 234,57 and -1 234,57

You’ve created a list of tuples, with each containing the name of a country and a locale. Each locale value is in the format "language_territory.codeset". So, "en_US.UTF-8" specifies that United States English is used and each character is encoded with the UTF-8 standard.

Your for loop iterates through each tuple in sample_locales and passes it into the set_locale() function’s locale parameter. This sets the locale on your computer. You also set the category parameter to LC_ALL to ensure that the locale is applied to all categories, including numbers, dates, and currencies. Finally, you intercept the locale’s name returned by the function to prevent it from being printed by your REPL.

Once your computer has a new locale set, the print() function shows you how each setting affects the display of positive and negative numbers in each of the countries in the sample. Once again, everything is rounded to two decimal places.

If you need more control over your formatting of numeric output, locale provides a format_string() function that uses format specifier codes similar to those you learned about earlier:

Python
>>> for name, loc in sample_locales:
...     _ = locale.setlocale(category=locale.LC_ALL, locale=loc)
...     print(
...         f"{name} uses --> {locale.format_string(
...             f="%10.2e", val=-123456.789, grouping=True
...         )}"
...     )
...
USA uses -->  -1.23e+05
Poland uses -->  -1,23e+05
UK uses -->  -1.23e+05
Czech Republic uses -->  -1,23e+05
Korea uses -->  -1.23e+05
Germany uses -->  -1,23e+05
France uses -->  -1,23e+05

Here, you’ve displayed each number using scientific notation in its local form. To do this, you passed f="%10.2e" into format_string(). When using this function, you must precede the format with a string formatting operator symbol (%). You also set the grouping parameter to True to make sure the thousands separators were applied.

If you want to display currencies, you can use the locale.currency() function:

Python
>>> for name, loc in sample_locales:
...     _ = locale.setlocale(category=locale.LC_ALL, locale=loc)
...     print(
...         f"{name} uses",
...         f"{locale.currency(val=123456.789, grouping=True)} and",
...         f"{locale.currency(val=-123456.789, grouping=True)}",
...     )
...
USA uses $123,456.79 and -$123,456.79
Poland uses 123 456,79 zł and -123 456,79 zł
UK uses £123,456.79 and -£123,456.79
Czech Republic uses 123 456,79 Kč and -123 456,79 Kč
Korea uses ₩123,457 and ₩123,457-
Germany uses 123.456,79 € and -123.456,79 €
France uses 123 456,79 € and -123 456,79 €

Here, you can see how different currencies are displayed in each of the countries sampled. You used the val and grouping parameters in currency() the same way as you did in format_string() earlier.

That’s it, you’ve finished. You now have a solid understanding of the main ways Python allows you to round numbers within strings.

Conclusion

In this tutorial, you covered a lot of ground that was hopefully interesting and a little challenging. You now know how to use Python’s format specification mini-language to round and display floats and Decimal objects in standard ways.

You also know how to deal with both scientific and complex numbers. You touched on some of the older ways to round numbers within strings, and saw how revolutionary the f-string syntax is. You even learned how to format numbers inside strings internationally.

Now that you’ve had a thorough workout formatting numbers, you should experiment further and consider delving deeper into f-strings and the format specification mini-language that supports them. There’s still lots more for you to learn.

Congratulations on completing this tutorial, and may all your strings be neat.

Take the Quiz: Test your knowledge with our interactive “Format Floats Within F-Strings” quiz. You’ll receive a score upon completion to help you track your learning progress:


Interactive Quiz

Format Floats Within F-Strings

In this quiz, you'll test your understanding of how to format floats within f-strings in Python. This knowledge will let you control the precision and appearance of floating-point numbers when you incorporate them into formatted strings.

🐍 Python Tricks 💌

Get a short & sweet Python Trick delivered to your inbox every couple of days. No spam ever. Unsubscribe any time. Curated by the Real Python team.

Python Tricks Dictionary Merge

About Ian Eyre

Ian is an avid Pythonista and Real Python contributor who loves to learn and teach others.

» More about Ian

Each tutorial at Real Python is created by a team of developers so that it meets our high quality standards. The team members who worked on this tutorial are:

Master Real-World Python Skills With Unlimited Access to Real Python

Locked learning resources

Join us and get access to thousands of tutorials, hands-on video courses, and a community of expert Pythonistas:

Level Up Your Python Skills »

Master Real-World Python Skills
With Unlimited Access to Real Python

Locked learning resources

Join us and get access to thousands of tutorials, hands-on video courses, and a community of expert Pythonistas:

Level Up Your Python Skills »

What Do You Think?

Rate this article:

What’s your #1 takeaway or favorite thing you learned? How are you going to put your newfound skills to use? Leave a comment below and let us know.

Commenting Tips: The most useful comments are those written with the goal of learning from or helping out other students. Get tips for asking good questions and get answers to common questions in our support portal.


Looking for a real-time conversation? Visit the Real Python Community Chat or join the next “Office Hours” Live Q&A Session. Happy Pythoning!

Keep Learning

Related Topics: basics python