Module 2 · Variables and Data Types
Problems: Variables and Types
In this lesson
- Choose the type a problem needs before you write its first line.
- Print a number in the exact shape a judge asks for, decimals included.
- Test a program at the edges of its constraints, where the type gives way.
Ten problems, graded against hidden tests. Eight of them you already met inside the five lessons. Two are new.
The last set was about printing. This one is about choosing. In almost every problem below there is a type that works and a type that quietly does not. The judge is the only one who will tell you which you picked.
What is new since the last problem set
In Module 1 every problem printed what you handed it. Here the answer has to survive a calculation first.
That changes what a wrong answer looks like. It will not be a missing space any more. It will be a number that is correct for small inputs and wrong for large ones.
Three problems below have an input at the very top of their range, and those are the tests that fail when you chose int. One has an input that fails when you chose double.
So read Constraints first, before the story, and decide the type there.
Choosing the type before you write a line
Four questions answer it every time.
- Can the value be negative? If no, unsigned is available, and lesson 2 says why you may still not want it.
- How big can it get, including during the calculation? Past about 2 billion is
long long. The sum of two allowed inputs counts as during. - Is it counting or measuring? Counting is an integer. Measuring may be a
double. - Is it money? Then it is counting, in paisa, in a
long long.
Question 2 is where most of the failures are. In safe-sum each input fits an int comfortably, and their sum does not.
So the rule is: look at the largest value your program will ever hold, not the largest value it will ever read.
Printing a number in the shape the judge wants
The judge compares text. Getting the right value and the wrong shape scores zero, exactly as in Module 1.
The specifiers these ten problems need
printf("%d\n", n) an int
printf("%lld\n", n) a long long
printf("%u\n", n) an unsigned int
printf("%.2f\n", x) a double, exactly two decimal places
printf("%.1f\n", x) a double, exactly one decimal place
printf("%lld.%02lld\n", taka, paisa) whole taka, then two digits of paisa
scanf("%d", &n) an int
scanf("%lld", &n) a long long
scanf("%lf", &x) a double, note the l
%fin ascanfreads afloatand ruins adouble. It is the single most common silent failure here.%.2frounds the stored value, which is not always the value you typed.%02lldpads with a zero to two digits, which is how 5 paisa prints as05.- Every starter below already has the correct
scanfline. Leave it alone.
Still no loop and still no condition
Nothing here needs if or for, and Modules 5 and 6 have not happened yet.
Three problems ask a yes or no question, and the answer is a comparison. A comparison is an expression worth 1 or 0, which lesson 4 showed you, so you print it with %d.
Two problems need a remainder, which is Module 4's % operator. Lesson 2's Exercise 4 built one out of /, * and -, and that is the intended route.
So if you reach for a construct you have not met, the problem is telling you to reread a lesson rather than to look ahead.
How to test before you submit
Four checks, and the second one is different from Module 1's.
- Run the sample. If the sample fails, nothing else matters.
- Run both ends of the constraints. Copy the largest allowed input from the statement and paste it in. This is the check that catches a wrong type.
- Count the characters of your output against the sample. Decimal places, spaces, commas.
- Ask what the answer should be for the largest input, on paper. If your program agrees with a number you worked out yourself, you are done.
So testing is not reading the program again. It is running it on the inputs the statement already told you exist.
The hint ladder
Every problem below carries three steps you open in order. Hint 1 names what to notice, Hint 2 describes the approach in words, and Solution explains the whole method in two paragraphs.
Opening a hint is recorded and costs you nothing. Type the program yourself afterwards, because reading is not the skill.
Read two values that each fit in an int and print a sum that does not.
#include <stdio.h>
int main(void)
{
int a = 0;
int b = 0;
scanf("%d %d", &a, &b);
long long sum = (long long)a + b;
printf("%lld\n", sum);
return 0;
}
4000000000
That output is for the input 2000000000 2000000000. The cast is on the left of the +, so the addition itself happens in the wide type. Move it outside and the answer is ruined before it is widened.
The shape four of these problems need: read a decimal, calculate, print with a fixed number of places.
#include <stdio.h>
int main(void)
{
double side = 0.0;
scanf("%lf", &side);
double area = side * side;
printf("%.2f\n", area);
return 0;
}
6.25
That output is for the input 2.5. Change the %lf to %f and the program prints 0.00 for every input, with no message from the compiler.
No decimal type anywhere. The decimal point is put back at the last moment, by printf.
#include <stdio.h>
int main(void)
{
long long total_paisa = 120050;
long long taka = total_paisa / 100;
long long paisa = total_paisa - taka * 100;
printf("%lld.%02lld\n", taka, paisa);
return 0;
}
1200.50
Drop the 02 and a balance of 1200 taka and 5 paisa prints as 1200.5. That is a wrong answer on a judge and an angry customer in a shop.
Where this is used
- Every programming contest. ICPC, Codeforces and the Progsity contest platform all judge this way. "Wrong answer on test 7" almost always means a type that was too narrow.
- Financial reconciliation. A bank's end of day job adds millions of amounts in the smallest unit and compares one integer with another. A single decimal type anywhere in that chain would end the comparison.
- Automated marking. The graded exercises in every later module of this track, and the Skill Test's build section, use the same judge as these ten problems.
- Golden-file tests. Compilers and command line tools are tested by running them and comparing their output with a saved file, character by character. GCC's own test suite works exactly like this.
Common mistakes
1. Widening after the damage.
long long sum = (long long)(a + b);
No message at either command line, and it fails only on the largest tests. The a + b inside the brackets is still int arithmetic, so it overflows and then the wreckage is widened. Put the cast on one operand.
2. Reading a double with %f.
double r = 0.0;
scanf("%f", &r);
Silent on the Playground, and it printed 0.000000 for every input on our run. scanf was told to write 4 bytes into an 8-byte box. It is %lf in a scanf, always.
3. Printing money as a double.
double balance = 120050 / 100.0;
printf("%.2f\n", balance);
No message, and it even prints 1200.50 here. It is still the wrong habit: lesson 3's till printed 2.67 where the shop expected 2.68, and paisa-ledger forbids a double for that reason.
4. Comparing two decimal values with ==.
printf("%d\n", a + b == c);
No message, and it passes the easy tests. It fails on 0.1 0.2 0.3, which is a hidden test of close-enough and is named in the statement so that nobody is ambushed.
Kenji is testing the input box of a new tool. He wants the smallest possible program that proves a number went in and came out again.
Input. One line with one integer n.
Output. One line: the number, one space, then the word stored.
Constraints. -1000000 <= n <= 1000000.
Sample. Input 42 gives 42 stored.
#include <stdio.h>
int main(void)
{
int n = 0;
scanf("%d", &n);
/* One printf. Nothing else. */
return 0;
}
Run in Compiler
Hint 1
The value is already in n when your first line runs. Nothing needs calculating.
Hint 2
One printf with a format string that holds %d, a space, the word stored and a newline.
Solution
Print n with %d, then one space, then the word, then \n. An int is the right type here because the constraints stop at a million, far inside its range.
The negative end of the range is a real hidden test. %d prints the minus sign for you, so nothing extra is needed, but run it once to see that for yourself.
Bob is sorting two boxes of books and the labels are on the wrong boxes. Swap them, using a third variable to park the first value.
Input. One line with two integers a b.
Output. One line with the two numbers in the other order, separated by exactly one space.
Constraints. -1000000 <= a, b <= 1000000.
Sample. Input 10 20 gives 20 10.
#include <stdio.h>
int main(void)
{
int a = 0;
int b = 0;
int spare = 0;
scanf("%d %d", &a, &b);
/* Three assignments, then one printf. */
return 0;
}
Run in Compiler
Hint 1
Assignment copies a value into a box and destroys what was there. Once a has been written over, the old a is gone.
Hint 2
Park the old a in spare first. Then a can take b, and b can take what you parked.
Solution
Three assignments in this order: spare gets a, then a gets b, then b gets spare. Print the two with one printf and a single space between the specifiers.
Printing b and then a in one line also passes every test, and teaches nothing. Do the three assignments first, then try the short way. One of them is a lesson about memory and the other is a lesson about typing.
Amara adds up two readings from a sensor. Each reading fits in an int, and their sum does not.
Input. One line with two integers a b.
Output. One line with their sum.
Constraints. 0 <= a, b <= 2000000000.
Sample. Input 2000000000 2000000000 gives 4000000000.
#include <stdio.h>
int main(void)
{
int a = 0;
int b = 0;
scanf("%d %d", &a, &b);
/* The inputs fit in an int. The answer does not. */
return 0;
}
Run in Compiler
Hint 1
Two billion fits in an int. Four billion does not, and the largest test asks for exactly that.
Hint 2
The sum needs a long long, and so does the addition itself. Widen one side with (long long) before the +.
Solution
Declare a long long for the answer and write (long long)a + b. Once one operand is wide, the whole addition is done wide, and the second value is widened to match it automatically.
Print it with %lld. Using %d there compiles silently on the Playground and prints a number unrelated to the sum. That is the failure lesson 2's second mistake describes.
Zara is cutting circular table mats and needs the area of each one, to two decimal places.
Input. One line with one decimal number r, the radius in centimetres.
Output. One line with the area, to exactly two decimal places.
Constraints. 0.01 <= r <= 1000.00. Use 3.14159265358979 for pi.
Sample. Input 2.5 gives 19.63.
#include <stdio.h>
int main(void)
{
double r = 0.0;
scanf("%lf", &r);
/* One printf, with %.2f. */
return 0;
}
Run in Compiler
Hint 1
Area is pi times the radius times the radius. Write the pi from the statement, all fifteen digits of it.
Hint 2
Everything here is a double, so nothing is done as whole-number work. The only trap is the format specifier in each direction.
Solution
Multiply the pi constant by r twice and print the result with %.2f. The smallest allowed radius gives an area of about 0.0003, which prints as 0.00, and that is the correct answer rather than a bug.
Leave the scanf line as given. A %f there writes four bytes into an eight-byte box, so r stays at zero and every test fails at once. The compiler never says a word about it.
A server has been running for a very large number of seconds. Zara wants that written out as days, hours, minutes and seconds.
Input. One line with one integer s, the number of seconds.
Output. One line: d days, h hours, m minutes, s seconds, with the four numbers filled in.
Constraints. 0 <= s <= 1000000000000. The words are not adjusted for 1; print them exactly as shown.
Sample. Input 90061 gives 1 days, 1 hours, 1 minutes, 1 seconds.
#include <stdio.h>
int main(void)
{
long long s = 0;
scanf("%lld", &s);
/* 86400 seconds in a day, 3600 in an hour, 60 in a minute. */
return 0;
}
Run in Compiler
Hint 1
Take the days out first, then work on what is left. Whole-number division gives you the count and throws the rest away.
Hint 2
After days = s / 86400, the leftover is s - days * 86400. Do the same thing twice more, with 3600 and then 60.
Solution
Four values, taken in order from the largest unit down. Divide by 86400 for the days, then subtract that many days worth of seconds. Divide the remainder by 3600 for the hours and subtract again. Divide by 60 for the minutes, and whatever is left is the seconds.
Every variable is a long long, because the input alone passes a trillion. Print the four with one printf and four %lld specifiers, and copy the commas and the words from the statement rather than typing them from memory.
Kenji's desk lamp starts off. Somebody flips the switch n times. Say whether the lamp is on at the end.
Input. One line with one integer n, the number of flips.
Output. One line: 1 if the lamp is on, otherwise 0.
Constraints. 0 <= n <= 1000000000. No loop is needed, and none is wanted.
Sample. Input 7 gives 1. Input 0 gives 0.
#include <stdio.h>
int main(void)
{
int n = 0;
scanf("%d", &n);
/* No loop. What is left over when n is divided by 2? */
return 0;
}
Run in Compiler
Hint 1
Flipping twice returns the lamp to where it was. Only the odd or even of n can possibly matter.
Hint 2
You need the remainder when n is divided by 2, and % is Module 4. Lesson 2's Exercise 4 built a remainder out of /, * and -.
Solution
Whole-number division throws the fraction away, so n / 2 counts the complete pairs of flips. Multiply that back by 2 and subtract it from n. What is left is 1 for an odd number of flips and 0 for an even one. That is already the answer, so print it with %d.
An int is wide enough here, because a billion is well inside its range and nothing is multiplied by anything large. The billion in the constraints is there to stop anybody counting the flips one at a time.
Amara is working out how much memory a table of readings will need, before she asks for it.
Input. One line with one integer n, the number of readings.
Output. One line with the number of bytes that n values of type double would take.
Constraints. 0 <= n <= 1000000000. Take the size from sizeof, never from a number you typed.
Sample. Input 10 gives 80.
#include <stdio.h>
int main(void)
{
long long n = 0;
scanf("%lld", &n);
/* Multiply by sizeof(double), print with %lld. */
return 0;
}
Run in Compiler
Hint 1
The answer for the largest input is eight billion, so the multiplication has to happen in a box that holds eight billion.
Hint 2
sizeof gives back a size_t, which is unsigned. Cast it to long long so that both sides of the multiplication are the type you chose.
Solution
Read n as a long long, as the starter already does, and multiply it by (long long)sizeof(double). Print the product with %lld. The brain teaser above is the version of this program that gets it almost right.
Writing the 8 yourself passes every test and is still the wrong answer to the exercise. The point of the module is that the size is a question for the machine. A program that asks keeps working when the machine changes.
Maria's shop keeps its ledger in paisa, never in taka, because whole numbers never drift. One day's row has an opening balance, one deposit and one withdrawal.
Input. One line with three integers: the opening balance, the deposit and the withdrawal, all in paisa.
Output. One line with the closing balance written as taka: the whole taka, a full stop, then exactly two digits of paisa.
Constraints. 0 <= opening, deposit <= 1000000000000000, and the withdrawal never takes the balance below zero. No double anywhere.
Sample. Input 100000 25050 5000 gives 1200.50.
#include <stdio.h>
int main(void)
{
long long opening = 0;
long long deposit = 0;
long long withdrawal = 0;
scanf("%lld %lld %lld", &opening, &deposit, &withdrawal);
/* Do every sum in paisa. Put the decimal point back only when you print. */
return 0;
}
Run in Compiler
Hint 1
The whole calculation is one addition and one subtraction, in paisa. The only interesting part is the last line.
Hint 2
Whole taka is the balance divided by 100. The paisa is what the division threw away, and it has to print as two digits even when it is 5.
Solution
Add the deposit and subtract the withdrawal, all in long long paisa. Then split: the taka is the balance divided by 100, and the paisa is the balance minus a hundred times the taka. Print the pair with %lld.%02lld, where the 02 pads to two digits with a leading zero.
The statement bans double for a reason you can check. Two of the hidden tests use balances near a quadrillion paisa. That is far past the fifteen digits a double keeps correctly, so a decimal version would lose the last paisa.
Zara is checking a column of totals. She wants to know whether the first two numbers add up to the third, without being fooled by the last decimal place.
Input. One line with three decimal numbers a b c.
Output. One line: 1 if a + b is within 0.000000001 of c, otherwise 0.
Constraints. -1000000 <= a, b, c <= 1000000. One hidden test is 0.1 0.2 0.3, and the answer there is 1.
Sample. Input 0.1 0.2 0.3 gives 1. Input 1.0 1.0 3.0 gives 0.
#include <stdio.h>
#include <math.h>
int main(void)
{
double a = 0.0;
double b = 0.0;
double c = 0.0;
scanf("%lf %lf %lf", &a, &b, &c);
/* One printf with %d. A comparison is already a 1 or a 0. */
return 0;
}
Run in Compiler
Hint 1
The difference can be positive or negative, and you only care about its size. fabs from <math.h> drops the sign.
Hint 2
A comparison is an expression worth 1 or 0, so the whole answer fits inside one printf with %d. No if is needed.
Solution
Work out a + b - c, take its size with fabs, and compare that against 0.000000001, which is written 1e-9. The comparison is already the answer, so hand it straight to printf with %d.
Without fabs you would need two comparisons, one in each direction, multiplied together as lesson 4 showed. Both pass. The maths library is linked on the Playground already, so fabs needs nothing but the include.
Zara is designing a column in a file format. She needs the narrowest signed fixed-width type that holds every value the column may carry.
Input. One line with two integers lo hi, the smallest and the largest value the column may hold.
Output. One line with a width in bits: 8, 16, 32 or 64.
Constraints. -9000000000000000000 <= lo <= hi <= 9000000000000000000. No condition and no loop are needed.
Sample. Input -200 300 gives 16. Input 0 5000000000 gives 64.
#include <stdio.h>
int main(void)
{
long long lo = 0;
long long hi = 0;
scanf("%lld %lld", &lo, &hi);
/* Lesson 4: a comparison is a 1 or a 0, and two of them multiplied is "both". */
return 0;
}
Run in Compiler
Hint 1
An int8_t holds -128 to 127, an int16_t holds -32768 to 32767, an int32_t holds about plus or minus 2.1 billion. Write those six numbers down first.
Hint 2
For each type, "the range fits" is two comparisons multiplied together. The three ranges nest inside each other. So the three answers can only be 1 1 1, 0 1 1, 0 0 1 or 0 0 0.
Solution
Build three flags. Each one is (lo >= the type's lowest) * (hi <= the type's highest), so each is 1 or 0. Because the ranges nest, the flags can only switch on in order, and counting them tells you how far down the ladder you may go.
Turn the count into a width without a condition. Start at 8 bytes and halve it once per flag that is 1, by dividing by 1 + flag three times. Multiply the bytes by 8 and print with %lld. Write the type limits with the LL suffix, so that the comparisons happen in the wide type rather than in an int.
Common doubts
Can I use
long longfor every problem here and stop thinking?For the integer problems, yes, and you would pass. The point of the module is that you can say why it was needed in three of them and not in the others.
Why does the judge accept an answer with no final newline?
Because the comparison ignores newlines at the very end and spaces at the end of a line. Print the newline anyway; most judges elsewhere expect it.
My decimal answer differs from the sample in the last place. Is that a wrong answer?
Here, yes, because these outputs are rounded to a fixed number of places and compared as text. If your last place is wrong, the calculation is wrong, usually a whole-number division somewhere.
Am I allowed to use
%orifeven though the lessons have not taught them?Nothing stops you, and your answer will pass. You will have skipped the exercise, which was to solve it with what you have.
How do I know which hidden test failed?
The result names the test number. Match it against the constraints. The last tests are usually the extremes, and an extreme that fails is almost always a type that is too narrow.
Key takeaways
- Read Constraints first and choose the type there, before the first line is written.
- The largest value your program holds matters, not the largest value it reads.
- Widen before the arithmetic, never after it, or the answer is already spoiled.
%lfreads adoubleand%fruins one, silently, on the Playground.- Money is whole paisa in a
long long, printed with%lld.%02lld. - A comparison is worth 1 or 0, so three of these problems need no
ifat all.
Next is the module test: ten questions over the five lessons, plus two of these problems again.
Module test
Ten questions on this module. Pass at 70%, and you can take it as many times as you like.
Take the module testEnd of lesson 6
Get every problem accepted, and the lesson is done.
0 of 10 problems accepted
Next: Module Test: Variables and Data Types