5
Arithmetic and Logical Expressions
Expressions are Cadence® SKILL language objects that also evaluate to SKILL objects. SKILL performs a computation as a sequence of function evaluations. A SKILL program is a sequence of expressions that perform a specified action when evaluated by the SKILL interpreter. The three types of primitive expressions in SKILL are constants, variables, and function calls. You can combine constants, variables, and function calls with operators to form arithmetic and logical expressions.
A constant is an expression that evaluates to itself. That is, evaluating a constant returns the constant itself. For example:
123
10.5
"abc"
A variable stores values used during the computation and returns its value when evaluated. For example:
a
x
init_var
When SKILL creates a variable, it gives the variable an initial value of unbound (that-value-which-represents-no-value). If the interpreter encounters an unbound variable, you get an error message. You must initialize all variables. For example:
myVariable = 5
You will encounter the unbound variable error message if you misspell a variable because the misspelling creates a new variable.
A function call applies the named function to the list of arguments and returns the result when called. For example:
f(a b c d)
abs(-123)
exit()
Creating Arithmetic and Logical Expressions
You can combine constants, variables, and function calls with infix operators such as less than (<), colon (:), and greater than (>) to form arithmetic and logical expressions. For example:
1+2
a*b+c
x>y
You can form arbitrarily complicated expressions by combining any number of primitive expressions (constants, variables, and function calls) and operators.
Role of Parentheses
Parentheses delimit the names of functions from their argument lists and delimit nested expressions. In general, the innermost expression of a nested expression is evaluated first, returning a value used in turn to evaluate the expression enclosing it, and so on until the expression at the top level is evaluated.
Parentheses resemble natural mathematical notation and are used in both arithmetic and control expressions.
Quoting to Prevent Evaluation
Occasionally you might want to prevent expressions from being evaluated. This is done by “quoting” the expression, that is, putting a single quote just before the expression.
Quoting Variables
Quoting is often used with the names of variables and their values. For example, putting a single quote before the variable a (that is, 'a) prevents a from being evaluated. Instead of returning the value of a, the name of the variable a is returned when 'a is evaluated.
Quoting Lists
You usually specify lists of data within a program by quoting. Quoting is necessary because of the common list representation used for both program and data. For example, evaluating the list (f a b c) leads to the interpretation that f is the name of a function and (a b c) is a list of arguments for f. By quoting the same list, '(f a b c), the list is instead treated as a data list containing the four elements f, a, b, and c.
Try It Out
The best way to understand evaluation and quoting is by trying them out in SKILL. An interactive session with the SKILL interpreter will help to clarify and reinforce many of the concepts just described.
Arithmetic and Logical Operators
All arithmetic operators are translated into calls to predefined SKILL functions. These operators are listed in the table below in descending order of operator precedence. The table also lists the names of the functions, which can be called like any other SKILL function.
Error messages report problems using the name of the function. The letters in the Synopsis column refer to data types. Refer to the Data Types for a discussion of data type characters.
| Type of Operator | Name of Function(s) | Synopsis | Operator |
|---|---|---|---|
The following table gives more details on some of the arithmetic operators.
Predefined Arithmetic Functions
In addition to the basic infix arithmetic operators, several functions are predefined in SKILL.
Bitwise Logical Operators
The bnot, band, bnand, bxor, bxnor, bor, and bnor operators all perform bitwise logical operations on their integer arguments.
| Meaning | Operator |
|---|---|
Bit Field Operators
Bit field operators operate on bit fields stored inside 32-bit words. To avoid confusion in naming bits, SKILL uses the uniform convention that the least significant bit in an integer is bit 0, with the bit number increasing as you move left in the direction of the most significant bit.
- You can select bit fields by naming the leftmost and the rightmost bits in the bit field or by just naming the bit if the bit field is only one bit wide.
- You can use either integer constants or integer variables to specify bit positions, but expressions are not allowed.
- All bit fields are treated as unsigned integers.
-
Use the
sxtdfunction for sign-extending a bit field.
Bit Field Examples
x = 0b01101 => 13

x<0> => 1
The contents of the rightmost bit of x is 1.
x<1> => 0
The contents of bit one of x is 0.
x<2:0> => 5
Extracts the contents of the rightmost three bits of x. These are 101 or 5 in decimal.
x<7:4> = 0b1010 => 173
Stores the bit pattern into the bits 4 through 7.

(x + 4)<4:3> => 2
Adds 4 to x, then extracts the 3rd and 4th bits. 173 plus 4 is 177. SKILL returns the result of the last expression, which is binary 10 or 2 decimal.

Calling Conventions for Bit Field Functions
Because of limitations in the grammar, only integer constants or names of variables are permitted inside the angle brackets. To use the value of an expression to specify a bit position, you must either first assign the value of the expression to a variable or directly call the bit field functions without using the less than (<), colon (:), and greater than (>) infix operators. The calling conventions for the bit field functions are as follows:
bitfield1(
x_value
x_bitPosition )
setqbitfield1(
s_name
x_newvalue
x_bitPosition )
bitfield(
x_value
x_leftmostBit
x_rightmostBit )
setqbitfield(
s_name
x_newvalue
x_leftmostBit
x_rightmostBit )
Mixed-Mode Arithmetic
SKILL makes a distinction between integers and floating-point numbers.
- Integer arithmetic is used if all the arguments given to an arithmetic operator are integers.
- Floating-point arithmetic is used if all arguments are floating-point numbers.
- When integers and floating-point numbers are mixed, SKILL uses integer arithmetic until it encounters a floating-point number. SKILL then switches to floating-point arithmetic and returns a floating-point number as the result.
See the following topics for more information:
- Floating-Point Issues
- Integer and Floating-Point Division
- Type Conversion Functions (fix and float)
- Comparing Floating-Point Numbers
Floating-Point Issues
Because IEEE floating-point numbers use what are essentially binary (base 2) fractions to represent real numbers, some functions that operate on floating-point numbers yield unexpected (and incorrect) results.
There are some floating-point numbers that the computer cannot represent as binary fractions. In these cases, the computer uses a binary (base 2) floating-point number to approximate your decimal (base 10) floating-point number. These approximations can lead to incorrect results for some functions that operate on floating-point numbers. Consider the following:
| Decimal fraction | Equivalent | Binary fraction | Result |
|---|---|---|---|
Binary representation can result in a loss of precision. Also, when a program does not save or retrieve the least significant bits of a number, that number loses precision. See also “Comparing Floating-Point Numbers” and “Type Conversion Functions (fix and float)”.
Integer and Floating-Point Division
The division operator requires special attention because
Type Conversion Functions (fix and float)
Before you can compare an integer to a floating-point number, you must convert both numbers to the same type (that is, integer or float).
The fix and fix2 functions converts a floating-point number and floating-point calculations, respectively, to an integer. The float function converts an integer to a floating-point number. If the argument given to fix or float is already of the desired type, the argument is returned. See Cadence SKILL Language Reference for more information about these functions.
fix function that yields an incorrect result. For example:Skill > fix(4.1 * 100)
409
Skill > fix((30 - 18.1) * 10)
118
real_fix.il) into your SKILL development environment to force correct results in cases like those shown above.procedure(real_fix(number)
let( (num_string fix_num)
sprintf( num_string, "%f" number)
fix_num = atoi(car(parseString(num_string)))
fix_num
)
);
Comparing Floating-Point Numbers
integer or float) and have identical values. Two floating-point numbers can appear the same when printed while differing internally in their least significant bits.Simple comparison rarely works for floating-point numbers. For example:
if( (a == b) println("same"))
You can compare against an acceptable tolerance range with more success as follows (assuming a is not zero):
if( (abs(a - b)/a < 1e-6) println("same"))
You can also use the fix2 for rounding the result in floating-point calculations. This function returns the largest integer not larger than the given argument.
You can think of a loop (such as for) as a special case of comparing floating-point numbers. Each time a program increments a floating-point loop variable, the number can lose precision resulting in a cummulative error. Under these circumstances, your loop might not execute the expected number of times.
Function Overloading
Some applications that are based on SKILL overload the arithmetic and/or bit-level functions with new or modified semantics. While sqrt(-1) normally signals an error in SKILL, it returns a valid result as a complex number when some applications are running.
Because the arithmetic and bit-level operators are just simpler syntax for calling their corresponding functions, by overloading these functions with extended semantics for new data types, you can use the same familiar notation in writing expressions involving objects of these new data types.
By overloading the plus, difference, times, and quotient functions for a complex-number data type, you can use +, -, *, and / in forming arithmetic expressions involving complex numbers as you normally do in writing mathematical formulas.
Integer-Only Arithmetic
In addition to standard arithmetic functions that can handle integers and floating-point numbers, SKILL provides several integer-only arithmetic functions that are slightly faster than the standard functions. These functions are named by prepending x to the names of the corresponding standard functions: xdifference, xplus, xquotient, and xtimes.
When integer mode is turned on using the sstatus function, the SKILL parser translates all arithmetic operators into calls on integer-only arithmetic functions. This results in small execution time savings and makes sense only for compute-intensive tasks whose inner loops are dominated by integer arithmetic calculations.
sstatus( integermode t)=> t
status( integermode )=> t
Checks the status of integermode and returns t if integermode is on. The default is off.
The internal variables are typically Boolean switches that accept only the Boolean values of t and nil. For efficiency and security, system variables are stored as internal variables that can only be set by status, rather than as SKILL variables you can set directly. Refer to “Names of Variables” for a discussion of internal variables.
True (non-nil) and False (nil) Conditions
Unlike C or other programming languages that use integers to represent true and false conditions, SKILL uses the nonnumeric special atom nil to represent the false condition. The true condition is represented by the special atom t or anything other than nil.
Relational Operators
The relational operators lessp (<), leqp (<=), greaterp (>), geqp (>=), equal (==), and nequal (!=) operate on numeric arguments and return either t or nil depending on the results.
Logical Operators
The logical operators and (&&), or (||), and null (!), on the other hand, operate on nonnumeric arguments that represent either the false (nil) or true (non-nil) conditions.
False/True Conditions Do Not Equal Constants 0 and 1
If you program in C, be especially careful not to interpret the false/true conditions as equivalent to the integer constants 0 and 1.
Testing for Equality and Inequality
You can also use the equal (==) and the nequal (!=) operators to test for the equality and inequality of nonnumeric atoms.
- Two atoms are equal if they are the same type and have the same value.
- Two lists are equal if they contain the same elements.
Controlling the Order of Evaluation
The binary operators && and || are often used to control the order of evaluation.
The && Operator
The && operator evaluates its first argument and, if the result is nil, returns nil without evaluating the second argument. If the first argument evaluates to non-nil, && proceeds to evaluate the second argument and returns that value as the value of the function.
The || Operator
The || operator also evaluates its first argument to see if the result is non-nil. If so, || returns that result as its value and the second argument is not evaluated. If the first argument evaluates to nil, || proceeds to evaluate the second argument and returns that value as the value of the function.
Testing Arithmetic Conditions
In addition to the six infix relational operators, several arithmetic predicate functions are available for efficient testing of arithmetic conditions. These predicates are listed in the table below.
| Synopsis | Result |
|---|---|
Differences Between SKILL and C Syntax
Arithmetic and logical expressions in SKILL are the same as in the C programming language with the following minor differences:
-
SKILL supports the following exponentiation operator:
**(two asterisks). -
The function
modreplaces the modulo operator “%” so that you do not have to use “%” for this infrequently-used function: Usemod(i j)instead ofi % j. -
The more general
if/then/elsecontrol construct in SKILL makes the conditional expression operators?and:obsolete. -
The indirection operator
*and the address operator&do not have any meaning in SKILL and are not supported. -
The set of bitwise operators is augmented by
~&(nand),~|(nor), and~^(xnor). -
Logical expressions that evaulate to false return the special atom
niland those that evaluate to true return any non-nilvalue (usually, the special atomt).
SKILL Predicates
The following predicate functions test for a condition:
For a list of predicate functions for testing the type of a data object, see “Type Predicates”.
The atom Function
atom checks if an object is an atom. Atoms are all SKILL objects (except nonempty lists). The special symbol nil is both an atom and a list.
atom( 'hello ) => t
x = '(a b c)
atom( x ) => nil
atom( nil ) => t
The boundp Function
boundp checks if a symbol is bound (has an assigned value).
x = 5 ; Binds x to the value 5.
y = 'unbound ; Unbinds y
boundp( 'x ) => t
boundp( 'y ) => nil
y = 'x ; Binds y to the constant x.
boundp( y ) => t ; Returns t because y evaluates to x, which is bound.
Using Predicates Efficiently
Some predicates are faster than others. For example, the eq, neq, memq, and caseq functions are faster than their close relatives the equal, nequal, member, and case functions.
The equal, nequal, member, and case functions compare values while the eq, neq, memq, and caseq functions test if the objects are the same. That is, they test whether the objects reside in the same location in virtual memory.
These functions result in quicker tests but might require that you alter your application to take advantage of them.
The eq Function
eq checks addresses when testing for equality. The eq function returns t if both arguments are the same object in virtual memory. You can test for equality between symbols using eq more efficiently than using the == operator. The following example illustrates the differences between equal (==) and eq for lists.
list1 = '( 1 2 3 ) => ( 1 2 3 )
list2 = '( 1 2 3 ) => ( 1 2 3 )
list1 == list2 => t
eq( list1 list2 ) => nil
list3 = cons( 0 list1 ) => ( 0 1 2 3 )
list4 = cons( 0 list1 ) => ( 0 1 2 3 )
list3 == list4 => t
eq( cdr( list3 ) list1 ) => t
aList = '( a b c ) => a b c
eq( 'a car( aList ) ) => t
The equal Function
-
If the arguments are the same object in virtual memory (that is, they are
eq),equalreturnst. -
If the arguments are the same type and their contents are equal (for example, strings with identical character sequence),
equalreturnst. -
If the arguments are a mixture of fixnums and flonums,
equalreturnstif the numbers are identical (for example,1.0and1).
This test is slower than using eq but works for comparing objects other than symbols.
x = 'cat
equal( x 'cat ) => t
x == 'dog => nil; == is the same as equal.
x = "world"
equal( x "world" ) => t
x = '(a b c)
equal( x '(a b c)) => t
The neq Function
neq checks if two arguments are not equal, and returns t if they are not. Any two SKILL expressions are tested to see if they point to the same object.
a = 'dog
neq( a 'dog ) => nil
neq( a 'cat ) => t
z = '(1 2 3)
neq(z z) => nil
neq('(1 2 3) z) => t
The nequal Function
nequal checks if two arguments are not logically equivalent and returns t if they are not.
x = "cow"
nequal( x "cow" ) => nil
nequal( x "dog" ) => t
z = '(1 2 3)
nequal(z z) => nil
nequal('(1 2 3) z) => nil
The member and memq Functions
These functions test for list membership. member tests using equal while memq uses eq and is therefore faster. These functions return a non-nil value if the first argument matches a member of the list passed in as the second argument.
x = 'c
memq( x '(a b c d)) => (c d)
memq( x '(a b d)) => nil
x = "c"
member( x '("a" "b" "c" "d")) => (“c” “d”)
memq('c '(a b c d c d)) => (c d c d)
memq( concat( x ) '(a b c d )) => (c d)
The tailp Function
tailp returns the first argument if a list cell eq to the first argument is found by cdr’ing down the second argument zero or more times, nil otherwise. Because eq is being used for comparison, the first argument must point to a tail list in the second argument for this predicate to return a non-nil value.
y = '(b c)
z = cons( 'a y ) => (a b c)
tailp( y z ) => (b c)
tailp( '(b c) z ) => nil
nil was returned because '(b c) is not eq the cdr( z ).
Type Predicates
Many predicate functions are available for testing the data type of a data object. The suffix p on the name of a function usually indicates that it is a predicate function. Type predicates appear in the table below.
| Function | Value Returned |
|
|
|
|
|
|
Return to top