double
and int
types, and need no further explanation. To support the other two types, the Ptolemy kernel contains a Complex
class and a Fix
class, which are described in the rest of this section.
4.2.1 The Complex data type
The Complex
data type in Ptolemy contains real and imaginary components, each of which is specified as a double precision floating-point number. The notation used to represent a complex number is a two number pair: (real, imaginary)-for example, (1.3,-4.5) corresponds to the complex number 1.3 - 4.5j. Complex
implements a subset of the functionality of the complex number classes in the cfront and libg++ libraries, including most of the standard arithmetic operators and a few transcendental functions. Constructors:
Complex()
Create a complex number initialized to zero-that is, (0.0, 0.0). For example,
Complex C;
Complex(double real, double imag)
Create a complex number whose value is (real, imag). For example,
Complex C(1.3,-4.5);
Complex(const Complex& arg)
Create a complex number with the same value as the argument (the copy constructor). For example,
Complex A(complexSourceNumber);
Basic operators:
The following list of arithmetic operators modify the value of the complex number. All functions return a reference to the modified complex number (*this
).
Complex& operator = (const Complex& arg)
Complex& operator += (const Complex& arg)
Complex& operator -= (const Complex& arg)
Complex& operator *= (const Complex& arg)
Complex& operator /= (const Complex& arg)
Complex& operator *= (double arg)
Complex& operator /= (double arg)
There are two operators to return the real and imaginary parts of the complex number:
double real() const
double imag() const
Non-member functions and operators:
The following one- and two-argument operators return a new complex number:
Complex operator + (const Complex& x, const Complex& y)
Complex operator - (const Complex& x, const Complex& y)
Complex operator * (const Complex& x, const Complex& y)
Complex operator * (double x, const Complex& y)
Complex operator * (const Complex& x, double y)
Complex operator / (const Complex& x, const Complex& y)
Complex operator / (const Complex& x, double y)
Complex operator - (const Complex& x)
Return the negative of the complex number.
Complex conj (const Complex& x)
Return the complex conjugate of the number.
Complex sin(const Complex& x)
Complex cos(const Complex& x)
Complex exp(const Complex& x)
Complex log(const Complex& x)
Complex sqrt(const Complex& x)
Complex pow(double base, const Complex& expon)
Complex pow(const Complex& base, const Complex& expon)
Other general operators:
double abs(const Complex& x)
Return the absolute value, defined to be the square root of the norm.
double arg(const Complex& x)
Return the value arctan(x.imag()/x.real()).
double norm(const Complex& x)
Return the value x.real() * x.real() + x.imag() * x.imag().
double real(const Complex& x)
Return the real part of the complex number.
double imag(const Complex& x)
Return the imaginary part of the complex number.
Comparison Operators:
int operator != (const Complex& x, const Complex& y)
int operator == (const Complex& x, const Complex& y)
4.2.2 The
fixed-point data type
The fixed-point data type is implemented in Ptolemy by the
Fix
class. This class supports a two's complement representation of a finite precision number. In fixed-point notation, the partition between the integer part and the fractional part-the binary point-lies at a fixed position in the bit pattern. Its position represents a trade-off between precision and range. If the binary point lies to the right of all bits, then there is no fractional part. Constructing Fixed-point variables
Variables of type Fix
are defined by specifying the word length and the position of the binary point. At the user-interface level, precision is specified either by setting a fixed-point parameter to a "(value, precision)" pair, or by setting a
precision
parameter. The former gives the value and precision of some fixed-point value, while the latter is typically used to specify the internal precision of computations in a star.Fix
objects either have the precision passed as an "x.y" or "m/n" string, or as two C++ integers that specify the total number of bits and the number of integer bits including the sign bit (that is, n and x). For example, suppose you have a star with a precision parameter named precision. Consider the following code:
if (x.invalid())
Error::abortRun(*this, "Invalid precision");
Fix
class. The error check verifies that the precision was valid.Fix
object which is specified by the constant FIX_MAX_LENGTH
in the file $PTOLEMY/src/kernel/Fix.h
. The current value is 64 bits. Numbers in the Fix
class are represented using two's complement notation, with the sign bit stored in the bits to the left of the binary point. There must always be at least one bit to the left of the binary point to store the sign.Fix
object contains information about its precision and error codes indicating overflow, divide-by-zero, or bad format parameters. The error codes are set when errors occur in constructors or arithmetic operators. There are also fields to specify
Fix
values are assigned to it-truncation is the default
Fix
type is still experimental.
Fix
or FixArray
. The precision is specified by an associated precision state using either of two syntaxes:
Fix
with precision 3.5.
Fix
types are available in Ptolemy as a type of Particle
. The conversion from an int
or a double
to a Fix
takes place using the Fix::Fix(double)
constructor which makes a Fix
object with the default word length of 24 bits and the number of integer bits as needed required by the value. For instance, the double
10.3 will be converted to a Fix
with precision 5.19, since 5 is the minimum number of bits needed to represent the integer part, 10, including its sign bit.To use the
Fix
type in a star, the type of the portholes must be declared as "fix
". Stars that receive or transmit fixed-point data have parameters that specify the precision of the input and output in bits, as well as the overflow behavior. Here is a simplified version of SDFAddFix
star, configured for two inputs:
AddFix
star supports any number of inputs.) By default, the precision used by this star during the addition will have 2 bits to the left of the binary point and 14 bits to the right. Not shown here is the state OverflowHandler, which is inherited from the SDFFix star and which defaults to saturate
-that is, if the addition overflows, then the result saturates, pegging it to either the largest positive or negative number representable. The result value, sum, is initialized by the following code:
go
method, we use sum to calculate the result value, thus guaranteeing that the desired precision and overflow handling are enforced. For example,
Fix
object until the begin method runs. In the begin method, it is given the precision specified by OutputPrecision. The go method initializes it to zero. If the go method had instead assigned it a value specified by another Fix
object, then it would acquire the precision of that other object-at that point, it would be initialized.
Fix
object has been initialized, its precision does not change as long as the object exists. The assignment operator is overloaded so that it checks whether the value of the object to the right of the assignment fits into the precision of the left object. If not, then it takes the appropriate overflow response is taken and set the overflow error bit.If a
Fix
object is created using the constructor that takes no arguments, as in the protected
declaration above, then that object is an uninitialized Fix
; it can accept any assignment, acquiring not only its value, but also its precision and overflow handler.The behavior of a
Fix
object on an overflow depends on the specifications and the behavior of the object itself. Each object has a private data field that is initialized by the constructor; when there is an overflow, the overflow_handler
looks at this field and uses the specified method to handle the overflow. This data field is set to saturate
by default, and can be set explicitly to any other desired overflow handling method using a function called set_ovflow(<keyword>)
. The keywords for overflow handling methods are: saturate
(default), zero_saturate
, wrapped
, warning
. saturate
replaces the original value is replaced by the maximum (for overflow) or minimum (for underflow) value representable given the precision of the Fix
object. zero_saturate
sets the value to zero.
go
method assigned the input to the protected member sum
, which has the side-effect of quantizing the input to the precision of sum
. We could have alternatively written the go
method as follows:
sum
.Some stars allow the user to select between these two different behaviors with a parameter called ArrivingPrecision. If set to
YES
, the input particles are not explicitly cast; they are used as they are; if set to NO
, the input particles are cast to an internal precision, which is usually specified by another parameter.Here is the (abbreviated) source of the
SDFGainFix
star, which demonstrates this point:
SDFGainFix
star and many of the Fix
stars are derived from the star SDFFix
. SDFFix
implements commonly used methods and defines two states: OverflowHandler selects one of four overflow handlers to be called each time an overflow occurs; and ReportOverflow, which, if true, causes the number and percentage of overflows that occurred for that star during a simulation run to be reported in the wrapup method.
Fix()
Fix
number with unspecified precision and value zero.Fix(int length, int intbits)
Fix
number with total word length of length
bits and intbits
bits to the left of the binary point. The value is set to zero. If the precision parameters are not valid, then an error bit is internally set so that the invalid
method will return TRUE
.Fix(const char* precisionString)
Fix
number whose precision is determined by precisionString
, which has the syntax "leftbits.rightbits", where leftbits is the number of bits to the left of the binary point and rightbits is the number of bits to the right of the binary point, or "rightbits/totalbits", where totalbits is the total number of bits. The value is set to zero. If the precisionString
is not in the proper format, an error bit is internally set so that the invalid
method will return TRUE
.Fix(double value)
Fix
with the default precision of 24 total bits for the word length and set the number of integer bits to the minimum needed to represent the integer part of the number value. If the value given needs more than 24 bits to represent, the value will be clipped and the number stored will be the largest possible under the default precision (i.e. saturation occurs). In this case an internal error bit is set so that the ovf_occurred
method will return TRUE
.Fix(int length, int intbits, double value)
Fix
with the specified precision and set its value to the given value
. The number is rounded to the closest representable number given the precision. If the precision parameters are not valid, then an error bit is internally set so that the invalid
method will return TRUE
.Fix(const char* precisionString, double value)
precisionString
instead of as two integer arguments. If the precision parameters are not valid, then an error bit is internally set so that the invalid()
method will return true when called on the object.Fix(const char* precisionString, uint16* bits)
Fix
with the specified precision and set the bits precisely to the ones in the given bits
. The first word pointed to by bits
contains the most significant 16 bits of the representation. Only as many words as are necessary to fetch the bits will be referenced from the bits
argument. For example: Fix("2.14",bits)
will only reference bits[0]
.Fix(const Fix& arg)
arg
.Fix(int length, int intbits, const Fix& arg)
Fix
argument and set to a new precision. If the precision parameters are not valid, then an error bit is internally set so that the invalid
method will return true when called on the object. If the value from the source will not fit, an error bit is set so that the ovf_occurred
method will return TRUE
.int len() const
int intb() const
int precision() const
int overflow() const
Fix
number. The possible codes are:0
- ovf_saturate
,1
- ovf_zero_saturate
,2
- ovf_wrapped
,3
- ovf_warning
,4
- ovf_n_types
.int roundMode() const
1
for rounding, 0
for truncation.int signBit() const
TRUE
if the value of the Fix
number is negative, FALSE
if it is positive or zero.int is_zero()
TRUE
if the value of the Fix
number is zero.double max()
double min()
double value()
Fix
number as a double.void setToZero()
Fix
number to zero.void set_overflow(int value)
void set_rounding(int value)
TRUE
for rounding, FALSE
for truncation.void initialize()
Fix
number to zero.
void set_ovflow(const char*)
void Set_MASK(int value)
set_rounding()
.
int compare (const Fix& a, const Fix& b)
Fix
numbers. Return -1 if a < b, 0 if a = b, 1 if a > b.
int ovf_occurred()
TRUE
if an overflow has occurred as the result of some operation like addition or assignment.int invalid()
TRUE
if the current value of the Fix
number is invalid due to it having an improper precision format, or if some operation caused a divide by zero.int dbz()
TRUE
if a divide by zero error occurred.void clear_errors()
Fix& operator = (const Fix& arg)
*this
does not have its precision format set (i.e. it is uninitialized), the source Fix
is copied. Otherwise, the source Fix
value is converted to the existing precision. Either truncation or rounding takes place, based on the value of the rounding bit of the current object. Overflow results either in saturation, "zero saturation" (replacing the result with zero), or a warning error message, depending on the overflow field of the object. In these cases, ovf_occurred
will return TRUE
on the result.Fix& operator = (double arg)
Fix
number and then assigned to *this
.
Fix& operator += (const Fix&)
Fix& operator -= (const Fix&)
Fix& operator *= (const Fix&)
Fix& operator *= (int)
Fix& operator /= (const Fix&)
Fix operator + (const Fix&, const Fix&)
Fix operator - (const Fix&, const Fix&)
Fix operator * (const Fix&, const Fix&)
Fix operator * (const Fix&, int)
Fix operator * (int, const Fix&)
Fix operator / (const Fix&, const Fix&)
Fix operator - (const Fix&) // unary minus
int operator == (const Fix& a, const Fix& b)
int operator != (const Fix& a, const Fix& b)
int operator >= (const Fix& a, const Fix& b)
int operator <= (const Fix& a, const Fix& b)
int operator > (const Fix& a, const Fix& b)
int operator < (const Fix& a, const Fix& b)
Fix
with all 64 bits before the binary point.
Fix
.
int compare(const Fix& a, const Fix& b)
This functions returns -1 if a < b, 0 if a = b, and 1 if a > b. The comparison is exact (every bit is checked) if the two values have the same precision format. If the precisions are different, the arguments are converted to doubles and compared. Since double
values only have an accuracy of about 53 bits on most machines, this may cause false equality reports for Fix
values with many bits.
operator int() const
Fix
number as an integer, truncating towards zero.operator float() const
operator double() const
void complement()
Fix
class defines the following enumerated values for overflow handling:
Fix::ovf_saturate
Fix::ovf_zero_saturate
Fix::ovf_wrapped
Fix::ovf_warning
set_overflow
method, as in the following example:
overflow_type
argument may be one of saturate
, zero_saturate
, wrapped
, or warning
.The rounding behavior of a
Fix
value may be set by calling
Fix::mask_truncate
, truncation will occur. If the argument is nonzero (for example, if it has the value Fix::mask_truncate_round
, rounding will occur. The older name Set_MASK
is a synonym for set_rounding
.The following functions access the error bits of a
Fix
result:
TRUE
if there have been any overflows in computing the value. The second returns TRUE
if the value is invalid, because of invalid precision parameters or a divide by zero. The third returns TRUE
only for divide by zero.