# AFix¶

## Description¶

Auto-ranging Fixed-Point, `AFix`, is a fixed-point class which tracks the representable range of values while preforming fixed-point operations.

Warning: Much of this code is still under development. API and function calls may change.

User feedback is appreciated!

## Declaration¶

AFix can be created using bit sizes or exponents:

```AFix.U(12 bits)        // U12.0
AFix.UQ(8 bits, 4 bits) // U8.4
AFix.U(8 exp, 12 bits) // U8.4
AFix.U(8 exp, -4 exp) // U8.4
AFix.U(8 exp, 4 exp)  // U8.-4

AFix.S(12 bits)        // S11 + sign
AFix.SQ(8 bits, 4 bits) // S8.4 + sign
AFix.S(8 exp, 12 bits) // S8.3 + sign
AFix.S(8 exp, -4 exp) // S8.4 + sign
```

These will have representable ranges for all bits.

For example:

`AFix.U(12 bits)` will have a range of 0 to 4095.

`AFix.SQ(8 bits, 4 bits)` will have a range of -4096 (-256) to 4095 (255.9375)

`AFix.U(8 exp, 4 exp)` will have a range of 0 to 256

Custom range `AFix` values can be created be directly instantiating the class.

```class AFix(val maxValue: BigInt, val minValue: BigInt, val exp: ExpNumber)

new AFix(4096, 0, 0 exp)     // [0 to 4096, 2^0]
new AFix(256, -256, -2 exp)  // [-256 to 256, 2^-2]
new AFix(16, 8, 2 exp)       // [8 to 16, 2^2]
```

The `maxValue` and `minValue` stores what backing integer values are representable. These values represent the true fixed-point value after multiplying by `2^exp`.

`AFix.U(2 exp, -1 exp)` can represent: `0, 0.5, 1.0, 1.5, 2, 2.5, 3, 3.5`

`AFix.S(2 exp, -2 exp)` can represent: `-2.0, -1.75, -1.5, -1.25, -1, -0.75, -0.5, -0.25, 0, 0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75`

Exponent values greater 0 are allowed and represent values which are larger than 1.

`AFix.S(2 exp, 1 exp)` can represent: `-4, 2, 0, 2`

`AFix(8, 16, 2 exp)` can represent: `32, 36, 40, 44, 48, 52, 56, 60, 64`

Note: `AFix` will use 5 bits to save this type as that can store `16`, its `maxValue`.

## Mathematical Operations¶

`AFix` supports Addition (`+`), Subtraction (`-`), and Multiplication (`*`) at the hardware level. Division (`\`) and Modulo (`%`) operators are provided but are not recommended for hardware elaboration.

Operations are preformed as if the `AFix` value is a regular `Int` number. Signed and unsigned numbers are interoperable. There are no type differences between signed or unsigned values.

```// Integer and fractional expansion
val a = AFix.U(4 bits)          // [   0 (  0.)     to  15 (15.  )]  4 bits, 2^0
val b = AFix.UQ(2 bits, 2 bits) // [   0 (  0.)     to  15 ( 3.75)]  4 bits, 2^-2
val c = a + b                   // [   0 (  0.)     to  77 (19.25)]  7 bits, 2^-2
val d = new AFix(-4, 8, -2 exp) // [-  4 (- 1.25)   to   8 ( 2.00)]  5 bits, 2^-2
val e = c * d                   // [-308 (-19.3125) to 616 (38.50)] 11 bits, 2^-4

// Integer without expansion
val aa = new AFix(8, 16, -4 exp) // [8 to 16] 5 bits, 2^-4
val bb = new AFix(1, 15, -4 exp) // [1 to 15] 4 bits, 2^-4
val cc = aa + bb                 // [9 to 31] 5 bits, 2^-4
```

`AFix` supports operations without without range expansion. It does this by selecting the aligned maximum and minimum ranges from each of the inputs.

`+|` Add without expansion. `-|` Subtract without expansion.

## Inequality Operations¶

`AFix` supports standard inequality operations.

```A === B
A =\= B
A < B
A <= B
A > B
A >= B
```

Warning: Operations which are out of range at compile time will be optimized out!

## Bitshifting¶

`AFix` supports decimal and bit shifting

`<<` Shifts the decimal to the left. Adds to the exponent. `>>` Shifts the decimal to the right. Subtracts from the exponent. `<<|` Shifts the bits to the left. Adds fractional zeros. `>>|` Shifts the bits to the right. Removes fractional bits.

## Saturation and Rounding¶

`AFix` implements saturation and all common rounding methods.

Saturation works by saturating the backing value range of an `AFix` value. There are multiple helper functions which consider the exponent.

```val a = new AFix(63, 0, -2 exp) // [0 to 63, 2^-2]
a.sat(63, 0)                    // [0 to 63, 2^-2]
a.sat(63, 0, -3 exp)            // [0 to 31, 2^-2]
a.sat(new AFix(31, 0, -1 exp))  // [0 to 31, 2^-2]
```

`AFix` rounding modes:

```// The following require exp < 0
.floor() or .truncate()
.ceil()
.floorToZero()
.ceilToInf()
// The following require exp < -1
.roundHalfUp()
.roundHalfDown()
.roundHalfToZero()
.roundHalfToInf()
.roundHalfToEven()
.roundHalfToOdd()
```

An mathematical example of these rounding modes is better explained here: Rounding - Wikipedia

All of these modes will result in an `AFix` value with 0 exponent. If rounding to a different exponent is required consider shifting or use an assignment with the `truncated` tag.

## Assignment¶

`AFix` will automatically check and expand range and precision during assignment. By default, it is an error to assign an `AFix` value to another `AFix` value with smaller range or precision.

The `.truncated` function is used to control how assignments to smaller types.

```def truncated(saturation: Boolean = false,
overflow  : Boolean = true,
rounding  : RoundType = RoundType.FLOOR)

def saturated(): AFix = this.truncated(saturation = true, overflow = false)
```

`RoundType`:

```RoundType.FLOOR
RoundType.CEIL
RoundType.FLOORTOZERO
RoundType.CEILTOINF
RoundType.ROUNDUP
RoundType.ROUNDDOWN
RoundType.ROUNDTOZERO
RoundType.ROUNDTOINF
RoundType.ROUNDTOEVEN
RoundType.ROUNDTOODD
```

The `saturation` flag will add logic to saturate to the assigned datatype range.

The `overflow` flag will allow assignment directly after rounding without range checking.

Rounding is always required when assigning a value with more precision to one with lower precision.