Popup YouTube Video
Sheerpower Logo

Mathematical and Logical Operators


Understanding Operators in Sheerpower

Operators are the symbols that perform operations on variables and values. They are the building blocks for doing math, making comparisons, and creating logic in your programs. This tutorial is broken down into three parts to make learning easy and fun.

Part 1: The Basics - Mathematical Operators

These are the operators you use for basic arithmetic.

+ (Addition) - (Subtraction) * (Multiplication) / (Division) ^ (Exponentiation, e.g., 2^3 is 8) ++ (Increment, adds 1 to a variable, e.g., counter++) -x (Negation, flips the sign, e.g., -price or -(cost - budget))

Example: Calculating a Shopping Bill

apple_price = 0.50 banana_price = 0.30 num_apples = 4 num_bananas = 6 ! Calculate the cost for each fruit total_apple_cost = apple_price * num_apples total_banana_cost = banana_price * num_bananas ! Calculate the subtotal subtotal = total_apple_cost + total_banana_cost print "Subtotal: $"; subtotal ! Add one more apple num_apples++ print "Number of apples is now: "; num_apples

++ works on a numeric variable. On a dynamic variable it looks at the value the variable holds when the statement runs: a number goes up by 1, and a string raises a Datatype mismatch exception that a when block can catch:

declare dynamic counter counter = 5 counter++ print counter counter = 'five' when exception in counter++ use print extext$; ' -- '; _string$ end when

Output:

6 Datatype mismatch -- COUNTER++: COUNTER holds a string -- ++ needs a number

A minus sign in front of a value, or of a whole expression in parentheses, flips its sign:

cost = 120 budget = 150 under_by = -(cost - budget) print "Under budget by: "; under_by

Output:

Under budget by: 30

Part 2: Making Decisions - Relational and Logical Operators

These operators are used to compare values and combine conditions, which is essential for making decisions in your code.

Relational Operators (for comparing values)

= (Equal to) <> (Not equal to) > (Greater than) < (Less than) >= (Greater than or equal to) <= (Less than or equal to)

Logical Operators (for combining true/false conditions)

AND (Returns true if BOTH conditions are true) OR (Returns true if AT LEAST ONE condition is true) NOT (Reverses the condition, true becomes false)

Example: Qualifying for a Discount

is_member? = true purchase_total = 75.00 ! Check if the customer gets a discount is_eligible? = is_member? OR purchase_total > 100.00 print "Is eligible for discount? "; is_eligible?

Logical Operators on Numeric Values (Bitwise Operations)

When used with numeric values, these same operators perform bitwise operations. Each bit of the number is processed individually.

AND (Bitwise AND — keeps bits that are set in BOTH values) OR (Bitwise OR — keeps bits set in EITHER value) NOT (Bitwise NOT — flips all bits) XOR (Bitwise exclusive OR — keeps bits that differ)

Example: Working with Flags

// Example bit flags read_flag = 1 // 0001 write_flag = 2 // 0010 execute_flag = 4 // 0100 // Combine flags using OR permissions = read_flag OR write_flag // 0011 // Test if write flag is set has_write? = (permissions AND write_flag) <> 0 // Toggle a flag using XOR permissions = permissions XOR write_flag // removes write_flag print "Permissions: "; permissions print "Has write? "; has_write?
Bitwise vs Logical — Why It Matters

Problem: Developers often assume AND/OR always operate on true/false values, which leads to confusion when numbers are involved.

Solution: In Sheerpower, the same operators work consistently across both boolean and numeric contexts. When applied to numbers, they operate at the bit level.

Efficiency: Bitwise operations are extremely fast and allow compact representation of multiple flags inside a single numeric value.

Advanced Logical Operators

Sheerpower also supports more advanced logical operators. Their behavior is easiest to understand using "Truth Tables."

Truth Table for XOR, IMP, and EQV
XY X XOR Y X IMP Y X EQV Y
TRUETRUE FALSE TRUE TRUE
TRUEFALSE TRUE FALSE FALSE
FALSETRUE TRUE TRUE FALSE
FALSEFALSE FALSE TRUE TRUE

Part 3: How It All Works - Order of Operations

Sheerpower, like all programming languages, follows a specific order when evaluating expressions. This is called operator precedence. Understanding this order is crucial for getting the correct results.

Order of Operations

  1. Parentheses ()
    Expressions inside parentheses are always evaluated first.
  2. Exponentiation ^
    Powers are calculated next.
  3. Multiplication * and Division /
    These are evaluated from left to right.
  4. Addition + and Subtraction -
    These are evaluated next, from left to right.
  5. Relational & Logical Operators
    Comparisons and logical operations are done last.

Example: In the expression 4 + 5 * 2, the multiplication 5 * 2 happens first, resulting in 10. Then the addition 4 + 10 is performed, giving a final result of 14. To change this, you would use parentheses: (4 + 5) * 2, which evaluates to 18.


For more common math functions, see Common Math Functions. For string functions, see String Manipulation Functions and Features

Part 4: The Pipeline Operator |>

The pipeline operator |> takes the value on its left and passes it to the function on its right as that function's first argument. It works with any function — math, string, or user-defined.

Without the pipeline operator, a chain of function calls must be nested, so you read it from the inside out. With |>, each step appears in the order it actually happens:

full_name$ = ' ada lovelace ' clean$ = full_name$ |> trim$() |> ucase$() print clean$

Output:

ADA LOVELACE

That is the same as ucase$(trim$(full_name$)), read left to right instead of inside-out. Empty parentheses mean “the piped value is the only argument”. When the function takes more arguments, the piped value is the first and the listed ones follow:

print 'abcdef' |> mid$(2, 3) word_count = 'the quick brown fox' |> elements(' ') print word_count
bcd 4
Precedence. The pipe binds more loosely than every other operator: everything to its left is evaluated first, and an operator written after the call applies to the call's result.

Piping makes sequences of operations easier to read and maintain by writing them in the order they execute.

x$ = ' hi ' a$ = x$ // start with x$, which we will transform |> trim$() // first trim it |> ucase$() // next uppercase it print a$ // "HI"

Your own routines. A routine that can be called as a function (one returning parameter) can be a pipe target. The piped value binds to the routine's first declared with parameter; defaults fill the rest, and you may still name the others:

tax = 1000 |> calc_tax() ! amount = 1000, tax_rate defaults print tax tax = 1000 |> calc_tax(tax_rate = 0.25) print tax routine calc_tax with amount, tax_rate = 0.1, returning tax tax = amount * tax_rate end routine
100 250

Naming the first parameter as well is a compile error (the pipe already supplies it), and the target must be a function: piping into an array, a table, or a routine with no with parameters is rejected.

Longer pipelines. A chain may continue onto the next line, either by starting the continuation line with |> (any indentation) or by ending a line with it:

report$ = full_name$ |> trim$() |> ucase$() |> mid$(1, 3) print report$
ADA
Note: a lone | keeps its traditional meaning at the end of a print list; it becomes the pipe only when a > follows immediately. A ! at the very start of a line is always a comment (so !==== banners are safe), !> is a comment everywhere — the legacy header and tail style — and mid-line != means the same as <>. parse$() returns |> as a token.

Part 5: NaN and Infinity

A REAL can hold two special values besides numbers: NaN ("not a number") and infinity, written _nan, _inf and -_inf. They are values you put in — a constant, val("NaN"), a CSV cell that reads Inf, a float field read from a table — and once present they travel through arithmetic by the IEEE 754 rules: anything with a NaN in it is NaN, _inf + 1 is _inf, _inf - _inf is NaN, 1 / _inf is 0. What Sheerpower does not do is manufacture them from ordinary numbers: 1 / 0 still raises DIVBY0, sqrt(-1) still raises, and an INTEGER can never hold one (NUMOVER). So a NaN in your data is always something that was put there, never a division that went wrong behind your back.

When the IEEE answer is the one you want, ask for it by name: div0inf(a, b) is the division that gives Inf or -Inf for a nonzero number over zero and NaN for 0 / 0, and the ordinary exact quotient otherwise — the companion of div0(a, b), which gives 0. Because it is a function, a reader can see exactly where an infinity may be born.

x = _nan big = _inf print x, big, -big ! NaN Inf -Inf print big + 1, big - big, 1 / big ! Inf NaN 0 print x = x, x <> x ! 0 1 -- a NaN is not even equal to itself print x < 1, x >= 1 ! 0 0 -- every comparison with a NaN is false, except <> print isnan(x), isinf(big) ! 1 1 -- the tests that do find them print val("NaN"), str$(-_inf) ! NaN -Inf -- the texts round-trip print div0inf(1, 0), div0inf(-1, 0), div0inf(0, 0) ! Inf -Inf NaN -- asked for by name print div0(1, 0), div0inf(6, 3) ! 0 2 -- div0 gives 0; a nonzero divisor is just division

The comparison rule is the one to remember: a NaN is equal to nothing, itself included, so if x = missing then is never true when both hold a NaN, and x <> missing is always true. The test that works is isnan(x), and the mask that works is isnan(v): v(isnan(v)) = 999 fills the holes, v(v = _nan) = 999 would change nothing. Writing the literal _nan on either side of = <> < <= > >= is therefore a compile error that names isnan(); the one place the literal belongs in a test is select case, where case _nan classifies a NaN. Sorting puts NaN after everything, including infinity, and treats every NaN as one value, so sort(), unique() and isin() all behave; every stats$ function propagates a NaN, and the array page shows how filter(v, isfinite(v)) skips them. They print as NaN, Inf and -Inf (sprintf$ honours a width, ignores a precision), and json$ writes null.

Part 6: Complex Numbers

A REAL can also hold a complex number — a real part and an imaginary part, each carried to 30 significant digits. There is nothing to declare: write the imaginary unit as a suffix on a number, 4i, and 3 + 4i is the complex number 3 + 4i in any real variable, array element or cluster field. A real beside a complex is promoted (2 * z), and a value that is complex stays complex — z - 4i is 3+0i, never 3 — so real(z) and imag(z) are the way back to ordinary numbers. The four arithmetic operators compute every product exactly and round each part once, so (1 + 2i) / (3 + 4i) is exactly 0.44+0.08i; abs(z) is the magnitude (a correctly rounded hypotenuse), conj(z) flips the sign of the imaginary part, arg(z) is the angle in radians, and complex(re, im) builds one from computed parts.

z = 3 + 4i w = 1 - 2i print z, w, 4i, -z ! 3+4i 1-2i 0+4i -3-4i print z + w, z - w, z * w, z / w ! 4+2i 2+6i 11-2i -1+2i print 2 * z, z - 4i, (2 + 3i) ^ 2 ! 6+8i 3+0i -5+12i -- promoted, never demoted; a whole-number power print abs(z), real(z), imag(z), conj(z) ! 5 3 4 3-4i print z = 3 + 4i, z = w, (3 + 0i) = 3 ! 1 0 1 -- = and <> compare both parts print typeof$(z) ! Name:Z, Dtype:Real, *Complex* print (1 + 2i) / 3 ! 0.3333333333333333+0.6666666666666667i -- 16 digits per part by default print sprintf$("%.30r", (1 + 2i) / 3) ! 0.333333333333333333333333333333+0.666666666666666666666666666667i print val("1e5+2.5e-3i"), complex(1.5, -2) ! 100000+0.0025i 1.5-2i -- the text reads back print round(arg(1i), 4), round(arg(-4), 4) ! 1.5708 3.1416

Two things a complex number does not have. It has no order: z < w raises NOORDER at run time ("complex values have no order — compare abs(), real() or imag()"), as do max, min and clamp, and an ordering written against a complex literal is a compile error. And it has no integer value: int, floor, ceil and round raise NUMOVER, like NaN. The functions do work: sqr, exp, log, the trig family and a non-integer power take a complex (the Processing Complex Numbers page shows them), and signed zeros are kept and shown (conj(-4 + 0i) prints -4-0i) because the branch cuts of those functions hinge on them. json$ writes a complex as the string "3+4i", and val() reads it back.

Summary: You've learned about the three main types of operators in Sheerpower: mathematical for calculations, relational and logical for decision-making, and the all-important order of operations that ensures your expressions are evaluated correctly — plus the pipeline operator |>, which lets a chain of function calls read in the order it happens, the IEEE special values NaN and infinity, and complex numbers, which any REAL can hold.

(Show/Hide Sheerpower Operators Takeaways)
Hide Description

    

       


      

Enter or modify the code below, and then click on RUN

Looking for the full power of Sheerpower?
Check out the Sheerpower website. Free to download. Free to use.