""" MACROGROK Q-format simulator — matches infer4.asm semantics. Q1.14: 1 sign + 1 int + 14 frac (range [-2, 2) but used as [-1,1) for INPUT) Q3.12: 3 int + 12 frac, Q7.24 32-bit ACC. """ WEIGHTS_Q1_14 = [2458, -1638, 819, 3277] BIAS_Q3_12 = -256 ALPHA_Q1_14 = 12288 # 0.75 def q1_14_to_float(x): return x / 16384.0 def float_to_q1_14(f): return max(-16384, min(16383, int(round(f*16384)))) def q3_12_to_float(x): return x / 4096.0 def infer4(input_q1_14): assert len(input_q1_14)==4 # DOT4 Q1.14*Q1.14 -> Q3.24 in 32-bit, >>4 per product acc = 0 # Q7.24 but we use Python int as Q3.24 accum for w, inp in zip(WEIGHTS_Q1_14, input_q1_14): prod = w * inp # Q2.28 prod_q3_24 = prod >> 4 # arithmetic shift acc += prod_q3_24 # ACC Q3.24 -> Q3.12 tmp0 = acc >> 12 # arithmetic tmp0 = tmp0 + BIAS_Q3_12 # SAT Q3.12 [-8192,8191] sat = False if tmp0 < -8192: tmp0=-8192; sat=True if tmp0 > 8191: tmp0=8191; sat=True tmp1 = 16384 if tmp0 >=0 else -16384 flags = 0 if tmp0>=0: flags|=0x4 if sat: flags|=0x2 # UPDATE_STATE 3/4 low-pass (STATE persists, init 0) global STATE try: STATE except NameError: globals()['STATE']=0 STATE = (3*STATE + tmp1)//4 # arithmetic, Q1.14 scale flags|=0x1 return STATE, tmp0, flags if __name__=="__main__": for vec in [[16384,0,0,0],[0,16384,0,0],[-16384,-16384,0,0],[8192,8192,8192,8192]]: out, score, flags = infer4(vec) print(f"in={[q1_14_to_float(x) for x in vec]} -> score Q3.12 {score} ({q3_12_to_float(score):.3f}) out Q1.14 {out} ({q1_14_to_float(out):.3f}) flags {flags:03b}")