sourcelibs/Fixed/src/Fixed.xtl
1⍝# Fixed: fixed-width two's complement numbers at a stated width, the arithmetic a w-bit circuit does
2⍝# Import it with an alias of your choice: "fx:" u_se< "Fixed".
3⍝# Put libs/Fixed/src on XETAL_PATH ("just path"); the reference is libs/Fixed/docs.
4⍝# Names with l: are exported; the others are private to this file.
5⍝#
6⍝# X_eTaL's Int is 64 bits wide; hardware is 8, 16 or 32. Until X_eTaL
7⍝# has sized integer types (ask F3 in docs/xetal-asks.md), the width is
8⍝# written in the program: 16 ᶠˣw̲rap x + y is what a 16-bit adder
9⍝# gives, and the evaluator computes exactly that, so it is the
10⍝# reference the circuit must match bit for bit. Every function takes
11⍝# the width first (one number) and works on whole arrays of any
12⍝# shape. A function of three arguments is used curried: bind the
13⍝# width once, ᵘa̲dd ← ᶠˣa̲dd 16, then x ᵘa̲dd y.
14
15⍝## The ranges
16
17⍝# l_owest w: the lowest signed value of w bits, -2^(w-1) (-128 at 8 bits).
18⍝# >> "fx:" u_se< "Fixed"
19⍝# >> fx:l_owest 8 16
20⍝# -128 -32768
21ˡl̲owest ← { w → n̲eg 2 ^ w − 1 }
22
23⍝# h_ighest w: the highest signed value of w bits, 2^(w-1) - 1 (127 at 8 bits).
24⍝# >> "fx:" u_se< "Fixed"
25⍝# >> fx:h_ighest 8 16
26⍝# 127 32767
27ˡh̲ighest ← { w → (2 ^ w − 1) − 1 }
28
29⍝## Wrapping
30
31⍝# w u_wrap x: x reduced to the unsigned range of w bits, 0 to 2^w - 1:
32⍝# the bit pattern a w-bit register holds, read unsigned.
33⍝# >> "fx:" u_se< "Fixed"
34⍝# >> 8 fx:u_wrap 100 200 -1 256
35⍝# 100 200 255 0
36ˡu̲wrap ← { w x → x m̲od 2 ^ w }
37
38⍝# w w_rap x: x reduced to the signed range of w bits: the same bits,
39⍝# read as two's complement (a pattern at or above 2^(w-1) is negative).
40⍝# >> "fx:" u_se< "Fixed"
41⍝# >> 8 fx:w_rap 100 200 -1 -129
42⍝# 100 -56 -1 127
43ˡw̲rap ← { w x →
44 u ← w ˡu̲wrap x
45 u − (2 ^ w) × u ≥ 2 ^ w − 1
46}
47
48⍝# w o_verflow? x: whether x does not fit in w signed bits, so a
49⍝# w-bit unit holding it has wrapped.
50⍝# >> "fx:" u_se< "Fixed"
51⍝# >> 8 fx:o_verflow? 127 128
52⍝# 0 1
53ˡo̲verflow? ← { w x → x ≠ w ˡw̲rap x }
54
55⍝# w s_at x: x clamped to the signed range (saturating arithmetic:
56⍝# what a DSP does instead of wrapping).
57⍝# >> "fx:" u_se< "Fixed"
58⍝# >> 8 fx:s_at 300 -300 5
59⍝# 127 -128 5
60ˡs̲at ← { w x → (ˡl̲owest w) m̲ax x m̲in ˡh̲ighest w }
61
62⍝## Arithmetic, wrapped: what a w-bit adder, subtractor, multiplier gives
63
64⍝# w a_dd x y: the sum wrapped to w bits, item by item, with scalar
65⍝# extension: what a w-bit adder gives. Bind the width once:
66⍝# u:a_dd := fx:a_dd 16, then x u:a_dd y.
67⍝# >> "fx:" u_se< "Fixed"
68⍝# >> u:a_dd := fx:a_dd 8
69⍝# >> 100 u:a_dd 100
70⍝# -56
71ˡa̲dd ← { w x y → w ˡw̲rap x + y }
72
73⍝# w s_ub x y: the difference wrapped to w bits (a w-bit subtractor).
74⍝# >> "fx:" u_se< "Fixed"
75⍝# >> u:s_ub := fx:s_ub 8
76⍝# >> -100 u:s_ub 100
77⍝# 56
78ˡs̲ub ← { w x y → w ˡw̲rap x − y }
79
80⍝# w m_ul x y: the product wrapped to w bits (a w-bit multiplier keeps
81⍝# the low w bits).
82⍝# >> "fx:" u_se< "Fixed"
83⍝# >> u:m_ul := fx:m_ul 8
84⍝# >> 16 u:m_ul 16
85⍝# 0
86ˡm̲ul ← { w x y → w ˡw̲rap x × y }
87
88⍝# w n_eg x: the negation wrapped (the lowest value is its own negation).
89⍝# >> "fx:" u_se< "Fixed"
90⍝# >> 8 fx:n_eg 5 -128
91⍝# -5 -128
92ˡn̲eg ← { w x → w ˡw̲rap n̲eg x }
93
94⍝## Bits
95
96⍝# w b_its x: the two's complement bits of each number, most
97⍝# significant first: a vector for one number, a row per number for a
98⍝# vector (as Bits' b_its, which reads numbers unsigned).
99⍝# >> "fx:" u_se< "Fixed"
100⍝# >> 8 fx:b_its 5 -1
101⍝# 0 0 0 0 0 1 0 1
102⍝# 1 1 1 1 1 1 1 1
103ˡb̲its ← { w x → o̲\ (w r̲eshape 2) e̲ncode w ˡu̲wrap x }
104
105⍝# v_alue bits: the signed number each row of bits (or the vector)
106⍝# stands for, the inverse of b_its; the width is the row's length.
107⍝# >> "fx:" u_se< "Fixed"
108⍝# >> fx:v_alue 1 1 1 1 1 0 1 1
109⍝# -5
110ˡv̲alue ← { bits → (t̲ally o̲\ bits) ˡw̲rap 2 d̲ecode o̲\ bits }
111
112⍝# w h_ex x: the bit pattern of each number as hexadecimal text, w/4
113⍝# digits: text for one number, a row per number for a vector.
114⍝# >> "fx:" u_se< "Fixed"
115⍝# >> 16 fx:h_ex -1 48879
116⍝# FFFF
117⍝# BEEF
118ˡh̲ex ← { w x → (1 + o̲\ ((w d̲iv 4) r̲eshape 16) e̲ncode w ˡu̲wrap x) s̲elect ⎕D c̲at 6 t̲ake ⎕A }