Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

C# & .NET (C# 14, .NET 10 LTS)  Module 2 – Types, variables and operators

double, float and decimal: money and rounding

Why 0.1 + 0.2 is not 0.3 in a C# double, why decimal gets it right, how Math.Round treats halves, and which type fits money, measurements and counts.

  • Beginner
  • 15 minutes
  • Examples run with .NET 10.0.401 and Python 3.14.8
  • By MySmartCoPilot
Free core, Pro depth: 6 of 15 minutes free
Free: double and float store binary fractions, so 0.1 is never exact · decimal stores decimal digits, so 0.1 and 0.2 add up to 0.3 · Math.Round sends halves to the even neighbour unless you choose otherwise. With a pass: 2 in-depth parts and 3 interview answers, about 9 minutes.

What you will learn

  • Explain why 0.1 + 0.2 is not exactly 0.3 in a double, and when that matters
  • Choose between float, double, decimal and a whole count of paise for a kind of value
  • Use Math.Round with a MidpointRounding mode chosen on purpose to round amounts
  • Build a repayment schedule whose rounded instalments add up to the loan exactly
  • Compare doubles with a tolerance, and handle NaN and infinity

Before you start

What you’ll learn · Before you start

What you will learn

  • Explain why 0.1 + 0.2 is not exactly 0.3 in a double, and when that matters
  • Choose between float, double, decimal and a whole count of paise for a kind of value
  • Use Math.Round with a MidpointRounding mode chosen on purpose to round amounts
  • Build a repayment schedule whose rounded instalments add up to the loan exactly
  • Compare doubles with a tolerance, and handle NaN and infinity

Before you start

On this page

In a C# double, 0.1 + 0.2 is 0.30000000000000004, and 0.1 + 0.2 == 0.3 is false. The same sum in decimal is exactly 0.3, and the choice between the two decides whether a bill adds up to the last paisa.

You will see what double and float really store, why decimal gets 0.1 right, how Math.Round treats a value that lies exactly halfway, and which type fits money, measurements and counts.

double and float store binary fractions, so 0.1 is never exact

double and float are binary floating point: A way of storing a number as a binary fraction times a power of two, used by float and double. Tenths and other decimal fractions have no exact binary form, so they are stored as the nearest value that fits. types of 64 and 32 bits. Halves, quarters and eighths have exact binary forms; a tenth has none, just as a third has no exact decimal one. So the literal 0.1 becomes the nearest double instead. This program prints what is stored, to 27 decimals:

sum.cs: 0.1 + 0.2 in double, float and decimal C# · sum/sum.cs
using System.Globalization;

// Every number prints with the invariant culture, the same on every computer.
CultureInfo.CurrentCulture = CultureInfo.InvariantCulture;

double a = 0.1;
double b = 0.2;
double sum = a + b;
Console.WriteLine($"double:  0.1 + 0.2 = {sum}");
Console.WriteLine($"         equal to 0.3: {sum == 0.3}");
Console.WriteLine($"         0.1 is stored as {a:F27}");
Console.WriteLine($"         0.2 is stored as {b:F27}");
// The two stored values added exactly, in decimal, from the 27 decimals printed above.
decimal exact = decimal.Parse(a.ToString("F27")) + decimal.Parse(b.ToString("F27"));
Console.WriteLine($"         their exact sum  {exact}");
Console.WriteLine($"         the sum is       {sum:F27}");
Console.WriteLine($"         0.3 is stored as {0.3:F27}");

float x = 0.1f;
float y = 0.2f;
Console.WriteLine($"float:   0.1 + 0.2 = {x + y}, equal to 0.3: {x + y == 0.3f}");

decimal m = 0.1m;
decimal n = 0.2m;
Console.WriteLine($"decimal: 0.1 + 0.2 = {m + n}, equal to 0.3: {m + n == 0.3m}");

Output

double:  0.1 + 0.2 = 0.30000000000000004
         equal to 0.3: False
         0.1 is stored as 0.100000000000000005551115123
         0.2 is stored as 0.200000000000000011102230246
         their exact sum  0.300000000000000016653345369
         the sum is       0.300000000000000044408920985
         0.3 is stored as 0.299999999999999988897769754
float:   0.1 + 0.2 = 0.3, equal to 0.3: True
decimal: 0.1 + 0.2 = 0.3, equal to 0.3: True

Recorded with .NET SDK 10.0.401 (runtime 10.0.12) on macOS 26 arm64. To run it yourself: dotnet run sum.cs

0.1 and 0.2 are stored a little above their true values and 0.3 a little below. Step through the addition:

Interactive · stage by stage How 0.1 + 0.2 becomes 0.30000000000000004 in a double Step through one addition with the values that sum.cs printed. Every number has 27 decimals, cut short here.
How 0.1 + 0.2 becomes 0.30000000000000004 in a double, step 5 of 5. Showing 0.1 is stored as, 0.2 is stored as, The exact sum, Rounded to a double, Printed and compared. In focus: Printed and compared.0.1 is stored as0.1000000000000000055511…0.2 is stored as0.2000000000000000111022…The exact sum0.3000000000000000166533…Rounded to a double0.3000000000000000444089…Printed and compared0.30000000000000004print

The drawing shows the whole scene; its 5 steps are listed below.

Every step as text
  1. 0.1 in binary. A double holds a binary fraction. 0.1 has no exact binary form, the way 1/3 has no exact decimal one, so the literal 0.1 is stored as the nearest double, a little above 0.1.
  2. 0.2 in binary. 0.2 is stored the same way, also a little above its true value. Both errors are tiny, in the 17th digit and beyond.
  3. The exact sum. The processor adds the two stored values. Their exact sum, 0.300000000000000016653345369 to 27 decimals, has more binary digits than a double can keep.
  4. Rounded once more. The sum is rounded to the nearest double, 0.300000000000000044408920985. That is not the double the literal 0.3 becomes: 0.3 is stored as 0.299999999999999988897769754, just below 0.3.
  5. Printed and compared. Printing a double shows the shortest text that turns back into the same double, 0.30000000000000004. The == operator compares the stored values, which differ, so 0.1 + 0.2 == 0.3 is false.

The default text of a double, 0.30000000000000004, is the shortest text that reads back as the same stored value, which is why the error shows. In a float the same sum printed 0.3 and even compared equal, because with the 24 significant bits of a float both sides happened to round to the same value: luck, not exactness.

Comparing doubles with ==

0.1 + 0.2 == 0.3 is false, so a test like if (total == 0.3) silently fails. Compare doubles with a tolerance chosen for the size of the values, such as Math.Abs(a - b) <= 1e-9, or keep the values in decimal.

decimal stores decimal digits, so 0.1 and 0.2 add up to 0.3

decimal is a 128-bit type that stores a whole number of up to 96 bits and a power of ten, from 0 to 28, to divide it by. 0.1m is 1 divided by 10, which is exact, so the same sum printed 0.3 and compared equal. The m suffix makes a literal a decimal, and C# refuses to mix it with a double silently:

mix/Program.cs C# · mix/Program.cs
// A price in decimal and a rate in double, mixed without a conversion.
decimal price = 99.99m;
double rate = 0.18;
decimal tax = price * rate;
decimal discount = 0.05;
Console.WriteLine(tax - discount);
Building mix Python · mix_session.py
from session import run

# mix multiplies a decimal by a double and stores a double literal in a decimal; it is built once.
run("""
cd mix
!dotnet build -v q
""")

Output

$ cd mix
$ dotnet build -v q
mix/Program.cs(4,15): error CS0019: Operator '*' cannot be applied to operands of type 'decimal' and 'double' [mix/App.csproj]
mix/Program.cs(5,20): error CS0664: Literal of type double cannot be implicitly converted to type 'decimal'; use an 'M' suffix to create a literal of this type [mix/App.csproj]

Build FAILED.

mix/Program.cs(4,15): error CS0019: Operator '*' cannot be applied to operands of type 'decimal' and 'double' [mix/App.csproj]
mix/Program.cs(5,20): error CS0664: Literal of type double cannot be implicitly converted to type 'decimal'; use an 'M' suffix to create a literal of this type [mix/App.csproj]
    0 Warning(s)
    2 Error(s)

Time Elapsed …
[exit status 1]

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 mix_session.py

CS0019 refuses decimal * double, and CS0664 refuses the double literal 0.05 in a decimal. Write 0.18m and 0.05m, or convert with an explicit cast where a double really arrives from elsewhere. How many digits does each type keep? One third, then two whole numbers just past what float and double can count exactly:

digits.cs: digits kept by Half, float, double and decimal C# · digits/digits.cs
using System.Globalization;

// Every number prints with the invariant culture, the same on every computer.
CultureInfo.CurrentCulture = CultureInfo.InvariantCulture;

Console.WriteLine("One third in each type:");
Console.WriteLine($"Half     {(Half)(1.0 / 3)}");
Console.WriteLine($"float    {1f / 3}");
Console.WriteLine($"double   {1.0 / 3}");
Console.WriteLine($"decimal  {1m / 3}");

Console.WriteLine();
Console.WriteLine("Whole numbers past the digits a type holds:");
Console.WriteLine($"16,777,217 as a float:            {(float)16_777_217:F0}");
Console.WriteLine($"9,007,199,254,740,993 as a double: {(double)9_007_199_254_740_993:F0}");
Console.WriteLine($"9,007,199,254,740,993 as decimal:  {9_007_199_254_740_993m}");

Output

One third in each type:
Half     0.3333
float    0.33333334
double   0.3333333333333333
decimal  0.3333333333333333333333333333

Whole numbers past the digits a type holds:
16,777,217 as a float:            16777216
9,007,199,254,740,993 as a double: 9007199254740992
9,007,199,254,740,993 as decimal:  9007199254740993

Recorded with .NET SDK 10.0.401 (runtime 10.0.12) on macOS 26 arm64. To run it yourself: dotnet run digits.cs

Type Size Digits kept Exact for 0.1
float 4 bytes about 6 to 9 no
double 8 bytes about 15 to 17 no
decimal 16 bytes 28 to 29 yes

A float turned 16,777,217 into 16,777,216, and a double lost the last 1 of 9,007,199,254,740,993, which a decimal kept. The 16-bit binary type Half kept only four digits of one third. Answer three questions about the values you store, and the table rules out the types that would get them wrong:

Interactive · answer and compare Which type for this number? Answer three questions about the values you store. The table rules out the types that would get them wrong.
Are the values money, or other amounts that people write with decimal fractions?
Do you need fractions of the smallest unit, such as interest rates or currency conversions?
Do you store millions of values, where the size of each value matters?

Answer the questions above: the table then marks what fits your case and says why the rest does not.

Four ways to store an amount or a measurement, against what each gets right
Type Exact for 0.10 Digits and range Size and speed
float Poor no: binary fractions, and 16,777,217 became 16,777,216 Poor about 6 to 9 digits Good 4 bytes a value
double Poor no: 0.1 + 0.2 printed 0.30000000000000004 Good about 15 to 17 digits, up to about 1.7 × 10^308 Good 8 bytes, and the usual choice where speed matters
decimal Good yes: 0.1m + 0.2m printed 0.3 Mixed 28 to 29 digits, but only up to about 7.9 × 10^28 Mixed 16 bytes, and slower than double
long of paise or cents Good yes: 10 paise is the whole number 10 Mixed whole units only, no fraction of a paisa Good 8 bytes, integer arithmetic
How the answers decide
  • “Are the values money, or other amounts that people write with decimal fractions?” Yes, money or decimal amounts: rules out float and double. A binary fraction cannot hold 0.10 exactly, so amounts drift: 0.1 + 0.2 printed 0.30000000000000004.
  • “Are the values money, or other amounts that people write with decimal fractions?” No, measurements or scientific values: adds a caveat to decimal and long of paise or cents. A measurement is not exact to begin with; double keeps 15 to 17 digits of it in 8 bytes.
  • “Do you need fractions of the smallest unit, such as interest rates or currency conversions?” Yes, fractions of a paisa or a cent: rules out long of paise or cents. A long of paise has no room for part of a paisa; decimal keeps 28 to 29 digits.
  • “Do you store millions of values, where the size of each value matters?” Yes, millions of values: adds a caveat to decimal. decimal takes 16 bytes a value, twice a double and four times a float.

In short: decimal, or a whole count of paise in a long, for money; double for measurements and science; float only when millions of values must stay small.

Checkpoint

Check: Binary fractions in double and float · decimal and choosing a type

  1. Question 1 of 2 What does Console.WriteLine(0.1 + 0.2 == 0.3) print for doubles?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: False

    0.1, 0.2 and 0.3 have no exact binary form. The sum of the two stored values rounds to a double that is not the one 0.3 becomes, so == is false.

  2. Question 2 of 2 A shop stores prices such as 199.99 and adds them into bills. Which type should hold the prices?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: decimal, or a long count of paise

    Money is written in decimal fractions and must add up exactly. decimal stores decimal digits; a long of paise stores a whole number. Either works; double and float do not.

Math.Round sends halves to the even neighbour unless you choose otherwise

Math.Round without a mode uses midpoint rounding: The rule for a value that lies exactly halfway between two results, such as 2.5 rounded to a whole number. MidpointRounding.ToEven, the default of Math.Round, picks the even neighbour; AwayFromZero picks the one further from zero. to the even neighbour, MidpointRounding.ToEven, which Microsoft’s documentation also calls banker’s rounding:

round.cs: halves, two decimal places and a sum of doubles C# · round/round.cs
using System.Globalization;

// Every number prints with the invariant culture, the same on every computer.
CultureInfo.CurrentCulture = CultureInfo.InvariantCulture;

Console.WriteLine($"Math.Round(2.5m)                     = {Math.Round(2.5m)}");
Console.WriteLine($"Math.Round(3.5m)                     = {Math.Round(3.5m)}");
Console.WriteLine($"Math.Round(2.5m, AwayFromZero)       = {Math.Round(2.5m, MidpointRounding.AwayFromZero)}");
Console.WriteLine($"Math.Round(10.125m, 2)               = {Math.Round(10.125m, 2)}");
Console.WriteLine($"Math.Round(10.125m, 2, AwayFromZero) = {Math.Round(10.125m, 2, MidpointRounding.AwayFromZero)}");

// 11.0 plus 0.1 five times is 11.5, a midpoint, in exact arithmetic.
double total = 11.0;
decimal exact = 11.0m;
for (int i = 0; i < 5; i++)
{
    total += 0.1;
    exact += 0.1m;
}
Console.WriteLine($"double:  {total} rounds to {Math.Round(total, MidpointRounding.AwayFromZero)}");
Console.WriteLine($"decimal: {exact} rounds to {Math.Round(exact, MidpointRounding.AwayFromZero)}");

Output

Math.Round(2.5m)                     = 2
Math.Round(3.5m)                     = 4
Math.Round(2.5m, AwayFromZero)       = 3
Math.Round(10.125m, 2)               = 10.12
Math.Round(10.125m, 2, AwayFromZero) = 10.13
double:  11.499999999999998 rounds to 11
decimal: 11.5 rounds to 12

Recorded with .NET SDK 10.0.401 (runtime 10.0.12) on macOS 26 arm64. To run it yourself: dotnet run round.cs

2.5 became 2, 3.5 became 4, and to two places 10.125 became 10.12. A bill that should say 10.13 needs MidpointRounding.AwayFromZero, passed on every call. The last two lines show that the type matters here too: adding 0.1 five times to 11.0 gave the double 11.499999999999998, just below the midpoint, which rounded to 11, while the decimal sum was exactly 11.5 and rounded to 12. MidpointRounding has five modes:

  • ToEven sends a value that lies exactly halfway to the even neighbour, and it is the default.
  • AwayFromZero sends a halfway value away from zero, the most widely known way to round.
  • ToZero drops the extra digits, like truncation.
  • ToNegativeInfinity always rounds down, like Math.Floor.
  • ToPositiveInfinity always rounds up, like Math.Ceiling.

Key idea

Keep money in decimal or in whole paise, round it with a mode you chose on purpose, and leave double for measurements, where a tiny error is part of the value anyway.

Checkpoint

Check: Math.Round and midpoint rounding

  1. Question 1 of 2 What does Math.Round(2.5m) return?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: 2

    The default mode is MidpointRounding.ToEven, banker's rounding: 2.5 goes to 2 and 3.5 to 4. Pass MidpointRounding.AwayFromZero when halves must go up.

  2. Question 2 of 2 In round.cs, 11.0 plus 0.1 five times rounded away from zero gave 11 as a double but 12 as a decimal. Why?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: The double sum is 11.499999999999998, below the midpoint, while the decimal sum is exactly 11.5

    A midpoint rule decides only values that lie exactly halfway. A double that drifted below or above the midpoint rounds by its drifted value, which is why money is summed in decimal before it is rounded.

Rounding money so that every total adds up, with the numbers Pro: opens with a Pro, Premium or Ultimate pass

Rounding one amount is easy; making many rounded amounts add up to the right total is where real bills go wrong. The runs below compare the five modes on the same values, build a loan schedule that reconciles to the paisa and round tax two ways.

In this part:

  • All five modes on the same values, and the bias of rounding halves up
  • An EMI schedule whose instalments add up to the loan
  • Rounding tax on each line or once on the invoice
  • Amounts that arrive as doubles, and the scale a decimal keeps

4 runnable examples (C#) · about 8 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Comparing doubles: tolerances, NaN and infinity, reproduced Pro: opens with a Pro, Premium or Ultimate pass

Floating-point errors grow as values are combined, and a few special values break the rules of ordinary comparison. The runs below measure the drift, compare three ways of testing two doubles for equality and walk through NaN, infinity and negative zero.

In this part:

  • Errors add up: 0.1 summed ten times and a million times
  • Equal enough: absolute, relative and both
  • NaN, infinity and negative zero

2 runnable examples (C#) · about 5 minutes

Opening this part with your pass…Pro part. It opens with the same pass as the part above.

Practise: make the double round like the decimal

Round the double sum to 12

Copy round.cs to your computer. Before the last rounding, round the double total to 10 decimal places with Math.Round(total, 10), then round that to a whole number away from zero. Predict what it prints, then run it with dotnet run round.cs.

The questions below come one at a time, each picked from the idea your answers so far leave weakest:

Practice that adapts to you

Practise this lesson

9 questions about 8 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.

  1. Question 1 of 9 Why can a double not store 0.1 exactly?

    Choose one answer.

    Show the answer: Question 1 of 9

    Answer: 0.1 has no exact binary fraction, just as 1/3 has no exact decimal one

    Halves, quarters and eighths are exact in binary; tenths are not. Every binary floating-point type, float and Half included, has the same limit.

  2. Question 2 of 9 Why does Console.WriteLine(0.1 + 0.2) print 0.30000000000000004 and not 0.3?

    Choose one answer.

    Show the answer: Question 2 of 9

    Answer: A double prints the shortest text that reads back as the same stored value, and 0.3 would read back as another double

    .NET prints a double with the fewest digits that round-trip. For the sum that needs 17 significant digits, because 0.3 itself is a different double.

  3. Question 3 of 9 float printed 0.1f + 0.2f as 0.3 and compared it equal to 0.3f. Does that make float the better choice for money?

    Choose one answer.

    Show the answer: Question 3 of 9

    Answer: No: float also stores binary fractions, and here both sides just happened to round to the same value

    A single comparison that happens to hold proves nothing about a type. Decide from what the type stores: binary fractions in float and double, decimal digits in decimal.

  4. Question 4 of 9 decimal tax = price * 0.18; with price a decimal fails to compile. Why?

    Choose one answer.

    Show the answer: Question 4 of 9

    Answer: 0.18 is a double literal, and C# does not mix decimal and double without a cast

    A real literal without a suffix is a double. The m suffix makes it a decimal: 0.18m.

  5. Question 5 of 9 digits.cs stored the whole number 9,007,199,254,740,993 in a double. What did it print, and why?

    Choose one answer.

    Show the answer: Question 5 of 9

    Answer: 9007199254740992: a double keeps about 15 to 17 digits, and past 2^53 it cannot hold every whole number

    Range and digits are different things. A double reaches enormous values but keeps only about 15 to 17 significant digits, so large whole numbers and money with many digits lose their last digits.

  6. Question 6 of 9 A sensor reports temperatures with two decimals, and you store millions of readings for charts. Which type fits best?

    Choose one answer.

    Show the answer: Question 6 of 9

    Answer: double, or float when size matters most

    Choose by what the values are: money must add up exactly, measurements need digits and speed. The table's questions lead to double here.

  7. Question 7 of 9 Which call rounds 10.125m to 10.13?

    Choose one answer.

    Show the answer: Question 7 of 9

    Answer: Math.Round(10.125m, 2, MidpointRounding.AwayFromZero)

    Pass both the number of digits and the mode: the default mode is ToEven, which most bills do not use.

  8. Question 8 of 9 Which MidpointRounding mode always rounds down, like Math.Floor?

    Choose one answer.

    Show the answer: Question 8 of 9

    Answer: ToNegativeInfinity

    ToEven and AwayFromZero round to the nearest value; ToZero, ToNegativeInfinity and ToPositiveInfinity round every value in one direction.

  9. Question 9 of 9 A service rounds each amount with Math.Round(amount, 2) in five different files. What is the main risk?

    Choose one answer.

    Show the answer: Question 9 of 9

    Answer: They all use the default ToEven, which may not be the rule the business needs, and changing it means finding every call

    A rounding rule is a business rule. Give it one home, with its mode stated, and test it with halfway values.

Interview questions

Fresher · Why does 0.1 + 0.2 == 0.3 come out false for C# doubles?

Because a double holds a binary fraction, and each of the literals 0.1, 0.2 and 0.3 lies between two such fractions. Each one becomes the nearest double: 0.1 and 0.2 a little above their true values, 0.3 a little below. The sum of the two stored values is rounded to the nearest double, 0.30000000000000004, which is not the double that 0.3 becomes, so == is false. It matters wherever values must be exact, above all money. Use decimal, whose 0.1m is exactly one tenth, or whole paise in a long; when doubles must be compared, use a tolerance instead of ==.

  1. Mid to senior

    An invoicing service keeps prices and totals as double, and finance reports totals that are off by a paisa now and then. Explain the cause, and how you would move the service to exact amounts without breaking the stored data or the API.

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page. First step and follow-ups

  2. Fresher to mid

    A customer's bill shows 10.12 where the customer worked out 10.13 from an amount of 10.125. The code calls Math.Round(amount, 2). Explain what happened, decide which rule the service should follow, and how you make that rule hold in every part of the code.

    Model answer, Pro. First step and follow-ups

  3. Fresher to mid

    A unit test asserts that a computed interest of type double equals 1234.56, and it fails because the value is 1234.5600000000002. How do you write the assertion, and when is an exact comparison of doubles still right?

    Model answer, Pro. First step and follow-ups

Key takeaways

  • double and float store binary fractions, so 0.1 + 0.2 is 0.30000000000000004 in a double and == against 0.3 is false.
  • decimal stores decimal digits and a power of ten, so 0.1m + 0.2m is exactly 0.3; it does not mix with double without a cast.
  • Money belongs in decimal or in whole paise, and measurements in double.
  • Math.Round sends halves to the even neighbour by default, so pass MidpointRounding.AwayFromZero when a bill needs 10.125 to become 10.13.

Terms in this lesson

binary floating point
A way of storing a number as a binary fraction times a power of two, used by float and double. Tenths and other decimal fractions have no exact binary form, so they are stored as the nearest value that fits.
midpoint rounding
The rule for a value that lies exactly halfway between two results, such as 2.5 rounded to a whole number. MidpointRounding.ToEven, the default of Math.Round, picks the even neighbour; AwayFromZero picks the one further from zero.

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.