## fixed

2017-01-24

A fixed-point number type.

# FIXED

A Common Lisp fixed-point numeric type package intended to be similar to the Ada language type. The focus is providing a useful abstraction for known reliable precision in a specific range. This package uses CLOS to encapsulate the underlying type.

If the reader macro is installed `(install-reader-macro)`

, then fixed point values can be read without floating point precision issues.

A small utility package (:fixed/real-time) provides a portable fixed-point type representing the internal real time.

Please let me know if you find this useful, or encounter issues.

## Usage

`;; Ordinary power-of-2 fixed point type that supports a resolution of 1/10. ;; This is represented by a 1/16 resolution value. > (defdelta foo 1/10) ;; Reader macro usage. > #q foo 1.25 #<FOO 5/4> ;; Fixed point type with precise resolution ;; This is represented by a 1/10 resolution value. > (defdelta bar 1/10 :small 1/10) ;; Adding range info > (defdelta foobar 0.01 :small 0.01 :low 0.00 :high 1.00) > (defparameter fb (make-foobar 0.5)) FB > fb #<FOOBAR 0.5> > (f+ fb (make-foobar 1/2)) #<FOOBAR 1.0> > (f+ fb (make-foobar 0.51)) ;; ERROR: The value 101 is not of type (MOD 101). > (setf (foobar fb) 0.49) #<FOOBAR 0.48999998> > (f+ fb (make-foobar 0.51)) #<FOOBAR 1.0> ;; Base 10 decimal types are simply created like this: > (fixed:defdecimal milli 3) MILLI ;; Using the make-milli function works...but comes with ;; floating point precision issues. > (make-milli 123456789.001) #<MILLI 123456782.336> 0.0 ;; Using the #q reader avoids floating point representation. > #q milli 123456789.001 #<MILLI 123456789.001>`

## Fixed-point Reader Macro

A fixed-point reader macro provides a method to input fixed-point literals in decimal form. The reader macro uses the Q format to define a fixed-point spec for the following value.

Install the reader macro as a Q dispatch on # with `(install-q-reader)`

.

e.g.

`;; Read in fixed-point literals that can be represented exactly by a Q8 spec. > #Q8 1.5 3/2 > #Q8 0.0078125 1/128 ;; Read in a fixed-point literal that can be represented exactly by a Q3 spec, and one that can't. > #Q3 1.5 3/2 > #Q3 0.0078125 ;; ERROR: 0.0078125 is not a #Q3`

Bounds checking can also be performed when the maximum number of useable bits are provided in the Q spec.

`;; Read in the most positive Q7.8 value. > #Q7.8 255.99609375 65535/256 > #Q7.8 256.0 ;; Error: 256.0 is not a #Q7.8 > #Q7.8 -256.0 -256`

Decimal fixed-point values can be read as well with `#QD`

and an optional spec value for digits.

e.g.

`> #QD 1.2345678901234567890 1234567890123456789/1000000000000000000 > #QD3 1.2345678901234567890 ;; ERROR: 1.2345678901234567890 is not a #QD3 > #QD3 1.234 617/500 > (float *) 1.234`

## Syntax

**defdelta** *name delta [:small small-value] [:low low-value] [:high high-value]*

=> *delta-name*

**defdecimal** *name power [:low low-value] [:high high-value]*

=> *decimal-name*

### Arguments

*name* --- a symbol

*delta* --- real number

*power* --- integer

*small-value*, *low-value*, and *high-value* --- optional real numbers

### Description

**defdelta** defines a fixed-point number type named *name* capable of representing a value with at least the accuracy provided in *delta*.

If *small-value* is provided in **defdelta**, it must be a real value no greater than *delta*. *small-value* is used as the minimum resolution scaling factor for the underlying value. When *small-value* is not provided, it will be chosen automatically and will be no larger than *delta*.

The *small-value* can be any real number, but rationals are recommended to avoid unexpected rounding behaviors for some of the operations. If necessary, consider entering a decimal value using the provided *#Q* reader macro. The following are equivalent.

`(defdelta a-fixed-type #qd 0.2 :small #qd 0.2) (defdelta a-fixed-type 1/5 :small 1/5)`

**defdecimal** defines a fixed-point number type named *name* capable of representing a base-10 decimal value with up to *power* number of digits to the right of the decimal. The *small-value* selected will be (expt 10 (- *power*)). *Note: This declaration is different from the Ada decimal type where you must still define the delta (but as a power-of-10), and you define the number of digits to use in the underlying type.*

*low-value* and *high-value* are both optional for **defdelta** or **defdecimal**, and are used to define the most-negative and most-positive values of the fixed point type.

**defdecimal** is essentially identical to **defdelta** when called with an identical *delta* and *small* that is a power of 10. The only difference is that values that have a **defdecimal** defined type will always be printed in decimal form. Values with a **defdelta** defined type may be printed as rationals.

### Operations

**defdelta** and **defdecimal** create a set of functions and generic methods associated with *name*.

Operation | Type | Description |
---|---|---|

(make-name value) | Function | Return a new instance of name with value rounded as necessary with *rounding-method* |

(make-name-value value) | Function | Return a new instance of name with the provided underlying value |

(name fp) | Function | Return the value in the name instance scaled by small |

(name-value fp) | Function | Returns the underlying value of an instance of name |

(set-name fp value) | Generic | Set the value of a name instance, rounding as necessary with *rounding-method* |

(set-name-value fp value) | Function | Set the underlying integer value of an instance of name |

(setf (name fp) value) | setf | Set the value of fp with rounding as necessary with *rounding-method* |

(setf (name-value fp) value) | setf | Set the underlying value of fp |

(small fp) or (small 'name) | Generic | Return the small when passed 'name or an instance of name |

(delta fp) or (delta 'name) | Generic | Return the delta when passed 'name or an instance of name |

(size fp) or (size 'name) | Generic | Return the number of bits required to store the underlying value of name when it is ranged, otherwise return :INFINITY |

### Constants

*+MOST-POSITIVE-NAME+* is defined for each fixed-point type and is either the most positive value, or :POSITIVE-INFINITY if unlimited.

*+MOST-NEGATIVE-NAME+* is defined for each fixed-point type and is either the most negative value, or :NEGATIVE-INFINITY if unlimited.

### Math Operations

Generic Function Predicates: f= f/= f> f>= f< f<=

Generic Arithmetic Operations: f+ f- f* f/

# FIXED/REAL-TIME

A utility package that implements a fixed-point type for internal real time.

`;; Get the current internal real time as a fixed point > (defparameter the-time (current-time)) THE-TIME > the-time #<REAL-TIME 3711125.080> ;; do some stuff ;; calculate deltat > (f- (current-time) the-time) #<REAL-TIME 15.616>`

# License

MIT