Perl Part I · Module 2 · Variables, Scalars, and Data Types
Numbers: Integers, Floats and How Perl Stores Them
Perl does not make you choose between an integer type and a floating-point type. There is one kind of number, it holds whole values and decimals alike, and most of the time you can simply use it. This chapter covers the parts worth knowing anyway: how to write a long number so a reader can check it, what division actually returns, and one genuinely important limit. Perl will print 0.3 as the sum of 0.1 and 0.2 while storing something very slightly different — so the printed answer and the stored value are not always the same thing, and a comparison can fail on numbers that look identical.
Foundation12 min readPerlPart IFundamentalsNumbersPrecision
Part I · Module 2 · Chapter 2.4 · Numbers: Integers, Floats and How Perl Stores Them
1. One Numeric Kind
In many languages you choose a numeric type before storing a value — an integer here, a floating-point number there. Perl does not ask. A scalar holds a number, and the same scalar can hold 3 or 72.5:
use strict;
use warnings;
my $count = 3;
my $average = 72.5;
my $cycles = 1_000_000;
print "count: $count\n";
print "average: $average\n";
print "cycles: $cycles\n";count: 3
average: 72.5
cycles: 1000000Three observations:
- No type was declared, consistent with Chapter 2.2:
mycreates a name, not a type. 72.5needed no special treatment. A decimal is just a number.1_000_000printed as1000000. The underscores are a readability aid for whoever reads your source; Perl ignores them entirely. Use them on anything long enough to miscount —1_000_000cannot be misread as100_000, and1000000can.
2. Division Returns a Decimal
One behaviour to settle early, because it differs from several other languages:
use strict;
use warnings;
print 1000 / 10, "\n";
print 10 / 4, "\n";
print int(10 / 4), "\n";100
2.5
210 / 4 is 2.5, not 2. Perl's / always performs the full division; it does not throw away the fractional part because both operands happened to be whole numbers. When you genuinely want a whole number, int() discards the fraction — note that it truncates toward zero rather than rounding, so int(2.9) is 2.
Also notice 1000 / 10 printed 100 rather than 100.0. Perl prints a number in the shortest form that represents it, so an integral result looks like an integer. That is a display decision, and the next section is about what happens when the display and the stored value disagree.
Operators in general — including %, ** and the compound forms — belong to Module 3. This chapter uses only what it needs.
3. The One Limit Worth Knowing
Decimal fractions are stored in binary, and most of them cannot be represented exactly — in the same way that one third cannot be written exactly in decimal, however many digits you allow. Perl's numbers are no exception.
Usually you never notice, because Perl prints the shortest representation:
use strict;
use warnings;
print 0.1 + 0.2, "\n";0.3That looks like a complete answer. Ask for more digits and it is not:
use strict;
use warnings;
printf "%.20f\n", 0.1 + 0.2;0.30000000000000004441The stored value is very slightly more than three tenths. printf with an explicit precision — Chapter 4.8 covers the formatting properly — simply stopped hiding it.
What print shows | 0.3 |
| What is actually stored | 0.30000000000000004441… |
Is it equal to 0.3? | no |
4. The Consequence
That last row is the part that matters. The following uses == to compare and ?: to pick one of two words — both belong to later chapters, and they appear here because asserting the result without demonstrating it would be a weaker lesson:
use strict;
use warnings;
print 0.1 + 0.2 == 0.3 ? "equal\n" : "not equal\n";not equalTwo numbers that both display as 0.3 are not the same number. This is not a Perl defect — essentially every language that uses binary floating-point behaves identically, and it is a property of representing decimals in binary rather than of any one implementation.
5. Why This Matters in Engineering Work
Two reasons, and they are not symmetrical.
Most of the numbers in a flow script are whole and safe. Error counts, warning counts, seeds, test numbers, cycle counts — these are integers, they compare exactly, and nothing in §3 applies to them. It would be misleading to leave you nervous about arithmetic in general.
The numbers that are not whole are exactly the ones that get compared to thresholds. A timing slack of -0.043, a utilisation of 0.78, a clock period of 3.333. Those are measured decimals read out of a tool's report, and a script that asks whether a slack is exactly equal to a limit will eventually answer wrongly on a value that displays identically to the limit. The correct question is whether it is below the limit, or within a tolerance of it — which is a comparison, not an equality.
That is the whole engineering content of this chapter, and it costs one habit: thresholds are compared with < and >, never with ==. Part II relies on it when it starts reading real report values.
6. Common Mistakes
Expecting 10 / 4 to be 2. Perl returns 2.5. Use int() if you want truncation — and know that it truncates rather than rounds.
Expecting int() to round. int(2.9) is 2. Rounding is a different operation, and printf with a precision is the usual way to display a rounded value.
Testing a calculated decimal with ==. The symptom is a comparison that fails on values that print identically, which looks like a Perl bug and is not. §4 is the demonstration.
Reading the printed value as the stored value. print shows the shortest form. If you need to know what is really there, ask for digits with printf.
Putting commas in a long number. 1,000,000 is not a number in Perl. Underscores are the separator: 1_000_000.
7. What You Should Be Able to Say Now
- Perl has one numeric kind, used for whole numbers and decimals alike, and a scalar does not declare which it holds.
- Underscores in a numeric literal are ignored by Perl and exist for the reader.
/returns the full result —10 / 4is2.5— andint()truncates toward zero rather than rounding.printshows the shortest representation, which can differ from the value actually stored.0.1 + 0.2prints0.3and is not equal to0.3; this is binary floating-point, not a Perl defect.- Whole numbers compare exactly; calculated decimals should be compared against a tolerance or with
<and>.
8. Exercise
Exercise 1 — readable literals. Declare a scalar holding ten million using underscores, and print it. Confirm the underscores do not appear in the output, and say who they were for.
Exercise 2 — predict three divisions. Without running it, write down what each prints: print 9 / 3;, print 9 / 2;, print int(9 / 2);. Then run all three and account for any surprise.
Exercise 3 — expose the stored value. Using printf "%.20f\n", ..., print the stored value of 0.1 + 0.2 and of 0.5 + 0.25. One is inexact and one is exact. Say which, and offer an explanation for the difference based on §3.
Exercise 4 — reason about a threshold. A script must report a failure when a measured slack is below -0.040. A colleague writes a check for slack equal to -0.040 and reports that it "sometimes misses". Explain in two sentences what is going wrong and what the check should ask instead.
9. Summary
- One numeric kind covers whole numbers and decimals; no type is declared.
- Underscores make long literals readable and are invisible to Perl.
/gives the full result, so10 / 4is2.5;int()truncates toward zero.- The printed value is the shortest representation, not necessarily the stored one.
0.1 + 0.2is not0.3— binary floating-point, shared by nearly every language.- Compare measured decimals with
<,>or a tolerance, never==. Whole numbers are unaffected.
Chapter 2.5 turns to the other common thing a scalar holds — text — and to the fact that everything read out of a file arrives as text whether it looks like a number or not.
Where this fits
Part of the Perl curriculum.
