Official

A - Batting Average Editorial by en_translator


The essence of this problem is “given a decimal, can you round it off to three decimal places and print it?”
Rounding off is common in everyday life, but rarely found in a contest, so some of you may have needed to solve it while searching around.
In this editorial, we will explain the solution while reviewing the property of decimals in programming.

Basics of floating-point data type

Many programming languages have a type to manage decimals, such as double in C and C++, and float in Python.
Such a decimal type manages the value in a somewhat special way. That is, when a variable of type double stores a value \(x\), what is stored in the memory is a pair \((a, b)\) of an integer \(a\) and a real number \(b\) satisfying \(1 \leq b \lt 2\) such that \(x = 2^{a} \times b\).
This is in contrast to integer types like int, which stores the value directly into the variable. Since the value \(a\) varies depending on the value \(x\), such a decimal type is called a floating-point data type.

  • For example, \(6 = 2^2 \times 1.5\), so it is stored as a pair \((a,b) = (2, 1.5)\).

One can convert from integers to decimals or vice versa with a cast operator.
For example, in C++, one can use double(x) to convert x of type int to type double. Thus, we can find \(S\) as follows:

int main() {
  int A, B;
  cin >> A >> B;
  double S = double(B) / double(A);
}

C

First of all, when printing a decimal of type double with printf function in C language, you may specify an output format specifier %lf as follows:

  double S = ((double)B) / ((double)A);
  printf("%lf", S);

In this problem, you need to print exactly \(3\) digits and round off the further digits. The requirement can be fulfilled by using an output format specifier %.3lf. (Technically, the value output here is sometimes different from the one resulting from rounding off, but it is not a problem here, so we put it aside.)

#include <stdio.h>
int main() {
  int A, B;
  scanf("%d %d", &A, &B);
  double S = ((double)B) / ((double)A);
  printf("%.3lf", S);
}

C++

There are several ways to print a decimal in C++. One is to use printf just as we did in C language. In C++, you can directly use the standard library in C (if you include proper libraries), so you just need to use the output format specifier mentioned above.
We also introduce a way to use std::cout. It is a bit difficult, but writing

cout << fixed << setprecision(3);

satisfies the desired condition.

  • Detailed explanation: fixed instructs it to avoid an exponential notation like 1e9; setprecision(n) instructs it to print the value rounded off to exactly \(n\) digits. (Again, it is a bit different from rounding off speaking precisely, but we ignore it as it does not matter here.)
#include <iomanip>
#include <iostream>
using namespace std;
int main() {
  int A, B;
  cin >> A >> B;
  double S = double(B) / double(A);
  cout << fixed << setprecision(3) << S << endl;
}

Python

In Python, you can specify the number of digits with a literal called f-string. For more details, see the reference listed below.

A, B = map(int, input().split())
S = B / A
print(f'{S:.3f}')

Bonus: why don’t we see “round off and print” in a contest?

The operation of “rounding off to \(n\) decimal places” is a basic operation that is used in every day life, such as the batting average; in math and science in school, you are often asked to round off the answer to some decimal places and write it down. However, in competitive programming, we seldom see the problem in the format “round off to \(3\) decimal places and print it;” instead, the judge is in a special style like:

the answer is considered correct if the absolute or relative error from the writer’s answer is at most \(10^{-6}\).

Why is that?

This is due to the property of floating-point data type. In the world of integers, when the value \(x\) is sandwiched as \(a \leq x \leq b\), there is only finite number of possible values \(x\); however, in the world of real number, there are infinite of them. In order to somehow treat in the framework of programming, the double type (and float type in Python too) pushes the value in the form of \(x = 2^{a} \times b\) by rounding off, so every operation causes a tiny error, which accumulates as you compute. (For example, every arithmetic operation on double type causes a relative error of about \(10^{-16}\).)

However, the possibility of errors is extremely in bad agreement with “round off and print.”
Suppose that this problem has a constraint \(1 \leq A \leq 100\) and consider the case where \((A, B) = (80, 1)\). Then, \(S = \frac{1}{80} = 0.0125\), and when rounded off to \(3\) decimal places, the answer is found to be 0.013. However, \(S\) may be a little smaller when computing \( B / A\), resulting in \(0.012499999999999999\); in that case, the \(4\)-th decimal place is \(4\), which yields a wrong answer 0.012 when rounded off.

As you can see in this example, rounding off has the property that “a subtle error may result in a different answer.” Therefore, if the problem asks to “print the answer rounded off,” that is a peculiar self-inconsistent problem that asks to “treat decimals while avoiding errors.”

  • The claim so far is not quite exact; a bit more precisely, what we need to care is not only the errors accumulating in the process of decimal computations but also an error due to the floating-point data types called rounding error, and the special rounding-off style called “rounding half to even” (or so-called “bankers’ rounding). (It is a difficult stuff, so we refrain from explaining here.)

Due to those reasons, the operation of “finding the rounded-off value,” which is commonly used in everyday life, is an unexpected difficulty in the world of programming. If we require rounding-off without such viewpoints, we may not be able to guarantee the validity of writers’ solution, or contestants with a descent knowledge of errors may have a great disadvantage. That is probably why well-behaved competitive-programming hosts like AtCoder would reject such problems beforehand and not see the light. However, this problem is an exception: under the given Constraints, we can prove that the answer is never represented in the form \(0.001 n + 0.0005\) (where \(n\) is an integer), and the decimal errors does not inflate so much to change the result of rounding off, and we think that is why it is exceptionally admitted to be set.

posted:
last update: