From 79ef07e4a7b3311782d5ce4e6fe1f15c08da054d Mon Sep 17 00:00:00 2001 From: Nick Patrick Date: Thu, 5 Jan 2017 21:32:49 -0600 Subject: [PATCH] Rearrange and update documentation. --- README.md | 176 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 97 insertions(+), 79 deletions(-) diff --git a/README.md b/README.md index 7be45fc..53769cf 100644 --- a/README.md +++ b/README.md @@ -2,83 +2,11 @@ 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. -Also provided is a utility package (:fixed/real-time) providing a portable fixed-point type representing the internal real time. +If the reader macro is installed ```(install-reader-macro)```, then fixed point values can be read without floating point precision issues. -## 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 +A small utility package (:fixed/real-time) provides a portable fixed-point type representing the internal real time. -**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. - -```lisp -(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/ +Please let me know if you find this useful, or encounter issues. ## Usage ```lisp @@ -86,6 +14,10 @@ f/ ;; This is represented by a 1/16 resolution value. > (defdelta foo 1/10) +;; Reader macro usage. +> #q foo 1.25 +# + ;; Fixed point type with precise resolution ;; This is represented by a 1/10 resolution value. > (defdelta bar 1/10 :small 1/10) @@ -107,6 +39,21 @@ FB # > (f+ fb (make-foobar 0.51)) # + +;; 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) +# +0.0 + +;; Using the #q reader avoids floating point representation. +> #q milli 123456789.001 +# + ``` ## Fixed-point Reader Macro @@ -163,10 +110,81 @@ e.g. 1.234 ``` -## Future Work -- Fixed-point reader macro improvements - - Read into a superclass of defined delta types -- Determine if the second return value from rounding operations is in the best form. +## 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. + +```lisp +(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