q_num/lib.rs
1//! # Q Number
2//!
3//! This library provides a macro to define a binary fixed-point number type for
4//! a specified number of integer bits and fractional bits.
5//!
6//! ## Summary
7//!
8//! This library provides the `define_q_num!` procedural macro (evaluated at
9//! compile time) to define a signed/unsigned binary fixed-point number type. It
10//! uses ARM-style Q notation: `Qm.n` (signed) or `UQm.n` (unsigned), where:
11//!
12//! - **m** is the number of integer bits, and
13//! - **n** is the number of fractional bits.
14//!
15//! Internally, the macro chooses the narrowest primitive integer type that can
16//! hold `m + n` bits, up to `u64` (unsigned) and `i64` (signed). More internal
17//! details are discussed below.
18//!
19//! ## Q Number Value
20//!
21//! A Q number's value is the ratio of the stored number (having n + m bits)
22//! and a fixed denominator (equal to 2 ^ n).
23//!
24//! For example, using the UQ3.2 specification, the bit pattern 0b10111
25//! represents the value 5.75. Keeping in mind the denominator is 2 ^ 2 = 4,
26//! there are two ways to see this:
27//!
28//! - 0b10111 / 4 == 23 / 4 == 5.75
29//! - 0b101 + 0b11 / 4 == 5 + 3/4 == 5.75
30//!
31//! ## Example Macro Usage
32//!
33//! Here is one example:
34//!
35//! ```
36//! # use q_num::define_q_num;
37//! define_q_num!(X, Q6.2);
38//! let a = X::try_from(13.75).unwrap();
39//! let b = X::try_from(-2.25).unwrap();
40//! let c = X::try_from(11.5).unwrap();
41//! assert_eq!(a + b, c);
42//! ```
43//!
44//! This defines a new type named `MyQ`, a signed fixed-point number represented
45//! internally with 8 bits:
46//!
47//! - 6 bits for the integer part
48//! - 2 bits for the fractional part
49//!
50//! ## Also Defined: Associated Methods
51//!
52//! The example above also defines the following floating-point conversions:
53//!
54//! - `MyQ::from(f64) -> MyQ`
55//! - `f64::from(MyQ) -> f64`
56//!
57//! It also defines the following getter and setter to access the internal
58//! representation:
59//!
60//! - `MyQ.to_bits() -> i8`
61//! - `MyQ::from_bits(i8) -> MyQ`
62//!
63//! ## Macro Variations
64//!
65//! Variations include (a) signed vs. unsigned, and (b) visibility.
66//!
67//! ### Unsigned Variation
68//!
69//! Use `UQ` to define an _unsigned_ fixed-point number:
70//!
71//! ```
72//! # use q_num::define_q_num;
73//! define_q_num!(MyNum, UQ11.5);
74//! ```
75//!
76//! The integer part uses two's complement representation.
77//!
78//! ### Visibility
79//!
80//! A caller can optionally include a visibility modifier for the new type:
81//!
82//! ```
83//! # use q_num::define_q_num;
84//! define_q_num!(pub MyNum, UQ11.5);
85//! ```
86//!
87//! ## Numerical Properties
88//!
89//! The value of a Q number is the ratio of the storage number and a fixed
90//! denominator.
91//!
92//! ## Internals
93//!
94//! The `define_q!` procedural macro defines a struct wrapping a primitive
95//! integer. This is an example of the [newtype pattern].
96//!
97//! For example, `define_q!(MyNum, Q6.2)` defines `struct MyNum(i8)`.
98//!
99//! The macro selects the narrowest primitive integer type that can hold the
100//! necessary number of bits. (For signed numbers, the integer part uses two's
101//! complement representation, meaning that the sign bit is already counted
102//! towards the integer part.)
103//!
104//! [newtype pattern]:
105//! https://doc.rust-lang.org/rust-by-example/generics/new_types.html
106//!
107//! ## See Also
108//!
109//! https://en.wikipedia.org/wiki/Q_(number_format)
110
111mod gen;
112mod literal;
113mod math;
114mod parse;
115mod types;
116
117use crate::gen::generate;
118use crate::parse::Input;
119use proc_macro::TokenStream;
120use syn::parse_macro_input;
121
122#[proc_macro]
123pub fn define_q_num(input: TokenStream) -> TokenStream {
124 let input = parse_macro_input!(input as Input);
125 match generate(input) {
126 Ok(token_stream) => token_stream.into(),
127 Err(e) => e.to_compile_error().into(),
128 }
129}