Skip to content
VLSI Mentor

Perl Part I · Module 3 · Operators and Expressions

Arithmetic Operators

Addition and multiplication hold no surprises, so this chapter spends its time on the four that do. Perl's division never truncates, which catches everyone arriving from C. The remainder operator takes the sign of its right-hand operand, which is the opposite of what C, Java and SystemVerilog do and therefore an actively wrong intuition rather than merely a missing one. Exponentiation groups right to left and binds tighter than a minus sign, so two plausible readings of a short expression give 512 and 64, or -4 and 4. And dividing by zero does not produce infinity or a warning — it stops the program. Every number in this chapter was produced by running the code.

Foundation14 min readPerlPart IFundamentalsArithmeticOperators

Part I · Module 3 · Chapter 3.2 · Arithmetic Operators

1. The Family

Perl has the arithmetic you expect, plus exponentiation:

OperatorMeaningExampleResult
+addition10 + 414
-subtraction10 - 46
*multiplication10 * 440
/division10 / 42.5
%remainder10 % 42
**exponentiation2 ** 8256

- also works as a unary operator, meaning negation: -$x. That matters more than it sounds, as §5 shows.

The first four rows are unremarkable except for the one in /. The rest of this chapter is about the rows that are not unremarkable.

2. Division Never Truncates

Azvya Education Pvt. Ltd.VLSI Mentor
division.pl
use strict;
use warnings;

print 10 / 4, "\n";
print int(10 / 4), "\n";
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
2.5
2

Perl has one numeric scalar type, as Chapter 2.4 established, and / gives you the real quotient. There is no separate integer division that triggers when both operands happen to be whole numbers — an important difference from C and from SystemVerilog, where 10 / 4 is 2.

If you want a whole number, say so with int():

Azvya Education Pvt. Ltd.VLSI Mentor
truncation.pl
use strict;
use warnings;

print int(2.9),  "\n";
print int(-2.9), "\n";
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
2
-2

int() truncates toward zero. It does not round. int(2.9) is 2, not 3, and int(-2.9) is -2, not -3. Rounding to the nearest whole number is a different operation, and printf is the usual way to do it — Chapter 2.5 introduced printf's formats, and Chapter 4.3 returns to them.

3. Dividing by Zero Stops the Program

Azvya Education Pvt. Ltd.VLSI Mentor
divzero.pl
use strict;
use warnings;

print "before\n";
print 1 / 0, "\n";
print "after\n";
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output — STDOUT
before
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output — STDERR
Illegal division by zero at divzero.pl line 5.
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output — exit status
255

Three things to read out of that, and the third is the one people get wrong.

  • It is fatal, not a warning. The program dies. You do not get infinity, NaN, or undef.
  • It is a run-time error. The file compiled cleanly — by the split from Chapter 1.5, perl -c would report this file as syntactically OK. perl -c cannot find this bug.
  • before was already printed, and after never was. The program did real work, emitted real output, and then stopped halfway. A script that writes a report file and dies at line 400 leaves a partial report on disk which looks like a complete one.

The practical consequence: a divisor that comes from your data — a test count, a cycle count, a file size — has to be checked before you divide by it. You do not have the tools to write that check yet; Chapter 3.9 and Module 8 give them to you. What you need now is to know that the check is necessary.

4. % Takes the Sign of Its Right Operand

This is the single most surprising behaviour in the chapter, and it is surprising in the worst way: readers arriving from another language have a confident, wrong expectation.

Azvya Education Pvt. Ltd.VLSI Mentor
modulo-signs.pl
use strict;
use warnings;

print  7 %  3, "\n";
print -7 %  3, "\n";
print  7 % -3, "\n";
print -7 % -3, "\n";
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
1
2
-2
-1

Read the middle two rows again. -7 % 3 is 2, not -1. 7 % -3 is -2, not 1.

ExpressionPerlWhat C, Java and SystemVerilog give
7 % 311
-7 % 32-1
7 % -3-21
-7 % -3-1-1

The rule, stated once: with integer operands, Perl's % produces a result whose sign follows the right-hand operand. The two languages agree whenever both operands are positive, which is why this can hide for years in code that only ever takes a remainder of positive numbers — and then surface the first time a value goes negative.

The common uses of % are safe because they involve positive numbers: "is this cycle count even" is $n % 2, and "every tenth test" is $i % 10 == 0. Both are fine as long as the left operand cannot go negative — and that is now a thing you know to confirm rather than assume.

5. ** Groups Right to Left, and Binds Tighter Than Minus

Exponentiation has two properties that no other arithmetic operator here has.

It is right associative. Where / and * group left to right — Chapter 3.1 §3 — ** groups the other way:

Azvya Education Pvt. Ltd.VLSI Mentor
power-grouping.pl
use strict;
use warnings;

print 2 ** 3 ** 2, "\n";
print 2 ** (3 ** 2), "\n";
print (2 ** 3) ** 2;
print "\n";
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output — STDOUT
512
512
8
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output — STDERR
print (...) interpreted as function at power-grouping.pl line 6.
Useless use of exponentiation (**) in void context at power-grouping.pl line 6.

The first two agree, which demonstrates the rule: 2 ** 3 ** 2 means 2 ** (3 ** 2) — that is 2 ** 9, or 512. It does not mean (2 ** 3) ** 2, which is 64.

The third line is a deliberate trap worth seeing: it printed 8, not 64. print (2 ** 3) ** 2; is parsed as (print(2 ** 3)) ** 2 — Perl took the parenthesis immediately after print as the argument list, printed 8, and then squared print's return value and discarded it. This is a classic Perl bite, and notice what saved you: use warnings from Chapter 1.11 printed print (...) interpreted as function and named the line. That is the diagnostic doing exactly the job Module 1 argued for — the wrong answer came with a free explanation. Writing print +(2 ** 3) ** 2; or print ((2 ** 3) ** 2); gives 64.

It binds tighter than unary minus:

Azvya Education Pvt. Ltd.VLSI Mentor
unary-minus.pl
use strict;
use warnings;

print -2 ** 2, "\n";
print (-2) ** 2;
print "\n";
print +(-2) ** 2, "\n";
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output — STDOUT
-4
-2
4
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output — STDERR
print (...) interpreted as function at unary-minus.pl line 5.
Useless use of exponentiation (**) in void context at unary-minus.pl line 5.

-2 ** 2 is -4. Perl reads it as -(2 ** 2) — exponentiate first, then negate. If you want the square of negative two you must write (-2) ** 2, which is 4, and the middle line shows the print-parenthesis trap biting again for good measure.

Mathematical convention agrees with Perl here, so this is not an oddity so much as a rule people forget. The fix is the one Chapter 3.1 recommended: parenthesise it and remove the question.

6. A Worked Example

Putting the chapter together on something you would actually write — summarising a regression run:

Azvya Education Pvt. Ltd.VLSI Mentor
summary.pl
use strict;
use warnings;

my $total  = 184;
my $passed = 177;

my $rate      = ($passed / $total) * 100;
my $failed    = $total - $passed;
my $full_tens = int($total / 10);
my $remainder = $total % 10;

printf "passed  : %d of %d (%.1f%%)\n", $passed, $total, $rate;
printf "failed  : %d\n", $failed;
printf "batches : %d full batches of ten, %d left over\n", $full_tens, $remainder;
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
passed  : 177 of 184 (96.2%)
failed  : 7
batches : 18 full batches of ten, 4 left over

Four points in nine lines of code:

  • ($passed / $total) * 100. The parentheses are not required — Chapter 3.1 showed that / and * group left to right anyway — and they are there so a reviewer does not have to recall that rule to check a percentage.
  • int($total / 10) is 18, and it is int() that truncated. 184 / 10 is 18.4; / produced the fraction and int() removed it. Writing $total / 10 and relying on %d to tidy it up would have worked here and would have hidden the decision.
  • $total % 10 is 4, and 18 * 10 + 4 is 184 — the quotient and remainder reconstruct the original, which is the check worth doing whenever you split a count like this. This is also a case where % is safe by construction: a test total cannot be negative.
  • %% prints a literal percent sign. printf treats % as the start of a conversion, so a percent sign in the output text has to be doubled.

7. Why This Matters in Engineering Work

Scripts in this trade compute throughputs, utilisation figures, pass rates and cycle budgets. The arithmetic is never hard; the failures come from three places, and this chapter has covered all three.

A divisor that can be zero. A regression with zero tests selected, a log with zero transactions, an empty file. $passed / $total is fine until $total is 0, and then the script dies at that line having already written part of its output. The failure is loud but late, and the partial artefact it leaves behind is the dangerous part.

An intuition carried in from another language. % is the clear case. An engineer who writes Perl on Monday and SystemVerilog on Tuesday has two different remainder operators with the same spelling, and the one place they differ is the one place nobody tests.

Assuming / truncates. A script computing "how many full batches" with $total / 10 gets 18.4, and whether that is a problem depends entirely on what happens to the value next. printf "%d" will show 18 and hide the issue; a comparison against an integer will not.

The habit that addresses all three: when a computed number will be acted on, check the divisor, parenthesise the grouping, and say explicitly whether you want truncation or rounding.

8. Common Mistakes

Expecting 10 / 4 to be 2. It is 2.5. Use int() if you want truncation, and know that int() truncates rather than rounds.

Expecting int() to round. int(2.9) is 2. For rounding, format with printf or add half before truncating — and be deliberate about negative values, where the two approaches differ.

Carrying C's % sign rule into Perl. -7 % 3 is 2 in Perl, -1 in C. The sign follows the right operand.

Reading 2 ** 3 ** 2 as 64. It is 512. ** is the only right-associative operator in this chapter.

Reading -2 ** 2 as 4. It is -4, because ** binds tighter than the minus sign.

Writing print (...) and expecting the parentheses to group arithmetic. They become print's argument list. §5 shows two cases of this; print +(...) is the usual fix.

Treating division by zero as something perl -c will catch. It is a run-time death in a file that compiles cleanly.

9. Exercise

Exercise 1 — the four signs. Run the §4 script and record all four results. Then write, in one sentence, the rule that explains them, and state what C would give for the second line.

Exercise 2 — truncate or round. Compute int(7 / 2) and printf "%.0f", 7 / 2. Record both, and explain why they differ. Then try int(-7 / 2) and printf "%.0f", -7 / 2 and say which of the two operations is sensitive to the sign.

Exercise 3 — make the grouping explicit. Predict 2 ** 2 ** 3 before running it. Then write the same expression twice with parentheses, once meaning what Perl means and once meaning the other thing, and confirm the two values.

Exercise 4 — the fatal divisor. Write a script that prints a line, then divides by a variable holding 0, then prints another line. Record STDOUT, STDERR and the exit status separately. Then run perl -c on the same file and state what that tells you about which errors a syntax check can find.

Exercise 5 — batch arithmetic. Given my $tests = 237; and a batch size of 16, print how many full batches there are and how many tests are left over. Verify your two numbers multiply and add back to 237.

10. Summary

  • / never truncates — 10 / 4 is 2.5. Perl has one numeric type and no implicit integer division.
  • int() truncates toward zero, it does not round: int(-2.9) is -2.
  • Division results are floating point, so they are approximations — don't test two computed values for exact equality.
  • Division by zero is a fatal run-time error with exit status 255, after any earlier output has already been written. perl -c cannot see it.
  • % takes the sign of its right operand. -7 % 3 is 2 in Perl and -1 in C and SystemVerilog.
  • ** is right associative — 2 ** 3 ** 2 is 512 — and binds tighter than unary minus, so -2 ** 2 is -4.
  • print followed immediately by ( takes that as its argument list, which quietly changes what gets printed.

Chapter 3.3 turns to the operators that work on text, including the one that uses the same symbol as multiplication to mean something entirely different.

Where this fits

Part of the Perl curriculum.