Binary Values and Operations
By default, arithmetic and string operations in NetRexx are carried out using the NetRexx string class, Rexx, which offers the robust set of operators described in Expressions and operators.
NetRexx implementations, however, may also provide primitive datatypes, as described in Types and Classes. These primitive types are used for compact storage of numbers and for fast binary arithmetic, features which are built-in to the hardware of most computers.
To make use of binary arithmetic, a class is declared to be a binary class by using the binary keyword on the class instruction. In such a class, literal strings and numeric symbols are assigned native string or primitive types, rather than NetRexx types, where appropriate, and native binary operations are used to implement operators where possible, as detailed below. Implementations may also provide a keyword on the options instruction that indicates that all classes in a program are binary classes.[1]
Binary classes should be used with care. Although binary arithmetic can have a considerable performance advantage over arithmetic that is not implemented in hardware, it can give incorrect or unexpected results. In particular, whole numbers (integers) are often held in fixed-sized data areas (of 8, 16, 32, or 64 bits), and overflowing the data area during a calculation can result in a positive number becoming negative and vice versa. Similarly, binary numbers that are not whole numbers (floating-point numbers) cannot exactly represent common numbers in the decimal system (0.1, 0.2, etc.), and hence can give unexpected results.
Operations in binary classes
In a binary class, the following (and only the following) rules differ from the rules for other classes:
- Dyadic operations in expressions
If the operands of a dyadic operator both have primitive numeric types[2] then binary operations are carried out. The type of the result is implementation defined, and is typically the type of the more precise of the two operands, or of some minimum precision.[3] Arithmetic operations follow the usual rules of binary arithmetic, as defined for the underlying environment of the implementation.
Note that NetRexx provides both divide and integer divide operators; in a binary class, the divide operator ('/') converts its operands to floating-point types and returns a floating-point result, whereas the integer divide operator ('%') converts its operands to integer types and returns an integer result. The remainder operator must accept both integer and floating-point types.
Logical operations (and, or, and exclusive or) apply to all the bits of the operands, and are not permitted on floating-point types.
- Prefix operations in expressions
If the operand of a prefix operator has a primitive numeric type, then the type of the result is the type of the operand, subject to the same minimum as dyadic operations. Prefix plus and minus follow the rules of dyadic operators (because they are defined as being zero plus or minus the operand) with the additional rule that if acting on a literal number (a constant in the program) then the result is also considered to be a literal constant. Logical not (prefix '\') does not apply to all the bits of its operand; instead, it changes a 0 to 1 and vice versa.
- Assignments
In assignments where the value being assigned is the result of an expression which comprises a string or number literal constant, the type of the result is defined as follows:
- Strings are given the native string type, even for a single-character literal.[4]
- Numbers are given the smallest possible primitive numeric type that will contain the literal without loss of information (or minimal loss of information for numbers with decimal or exponential parts). If this is smaller than the implementation-defined minimum precision used for the result of adding the literal to 0, then the type of that minimum precision is used.
If the constant is an integer, and no primitive integer binary type has sufficient precision to hold the number without loss of information, then the number is treated as a literal string (that is, as though it were enclosed in quotes). NetRexx arithmetic would then be used if it were involved in an arithmetic operation.
These rules can apply in assignment instructions, the initial assignment to the control variable in the loop instruction, or the assignment of a default value to the argument of a method; the result type may define the type of the variable (if new, or a method argument).
- Control variables in loops
In the loop instruction, if the control variable has a primitive integer type, and the increment (by value) has a primitive integer type, then binary arithmetic will be used for stepping the control variable, following the rules for binary arithmetic in expressions described above.
Similarly, if the control variable has a primitive integer type, and the end (to) value has a primitive integer type, then binary arithmetic will be used for the comparison that tests for loop termination.
- Numeric instruction
The numeric instruction does not affect binary operations. It has the usual effects on operations carried out using NetRexx arithmetic.
Note: At all times (whether in binary classes or not) implementations may use primitive types and operations, and techniques such as late binding of types, as an optimization providing that the results obtained are identical to those defined in this language definition.
Binary constructors
NetRexx provides special constructors for implementation-defined primitive types that allow bit-wise construction of primitives. These binary constructors are especially useful for manipulating the binary encodings of individual characters.
The binary constructors follow the same syntax as other constructors, with the name being that of a primitive type. All binary constructors take one argument, which must have a primitive type.
The bits of the value of the argument are extended or truncated on the left to the same length as the bits required for the type of the constructor (following the usual binary rules of sign extension if the argument type is a signed numeric type), and a value with the type of the constructor is then constructed directly from those bits and returned.
Example:
This example illustrates types from the reference implementation, with 32-bit signed integers of type int and 16-bit Unicode characters of type char.
i=int 77 -- i is now the integer 77
c=char(i) -- c is now the character 'M'
j=int(c) -- j is now the integer 77
Note that the conversion
j=int c
would have failed, as 'M' is not a number.