Racket/Posits

Från Täpp-Anders
Hoppa till navigeringHoppa till sök
#lang racket

;;;;;
;; En enkel implementation av Posists eller Unum typ III
;; i Racket för skojs skull och för att lära sig mer
;;
;; Täpp-Anders Sikvall anders@sikvall.se 2026-04-04
;;
;; Posit representeras som ett heltal (bitmönster, 0 till 2^n-1).
;; Parametrar: n = totalt antal bits, es = antal exponent-bits (t.ex. n=16, es=1 för posit16).
;; Innehåller:
;;  * Konvertering till/från generiska tal (number->posit och posit->number)
;;  * Addition, subtraktion, multiplikation och division av posits
;;
;; Aritmetiken är implementerad via konvertering till reellt tal + operation + tillbaka till posit
;; (enkel approach som bevarar tapered precision via rounding vid varje steg).
;; Full bit-exakt aritmetik (alignera regime/exponent etc.) är mer komplex och lämnas som utökning.
;; Algoritmen för number->posit är översatt direkt från Gustafsons x2p i "Posits4.pdf".
;; posit->number använder full fält-extraktion (regime, exponent, fraction).

(define (number->posit x n es)
  ;; x2p-algoritmen från Gustafson (översatt till Racket)
  ;; Returnerar posit-bitmönster som heltal.
  (cond
    [(= x 0) 0]
    [(infinite? x) (arithmetic-shift 1 (- n 1))]
    [else
     (let* ([nbits n]
            [npat (expt 2 nbits)]
            [useed (expt 2 (expt 2 es))]
            [y (abs x)]
            [p 0]
            [i 0]
            [e-val (- (expt 2 es) 1)])
       ;; Northeast/southeast quadrant (regime-byggnad)
       (if (>= y 1)
           (begin
             (set! p 1)
             (set! i 2)
             (let loop-regime ()
               (when (and (>= y useed) (< i nbits))
                 (set! p (+ (* 2 p) 1))
                 (set! y (/ y useed))
                 (set! i (add1 i))
                 (loop-regime)))
             (set! p (* 2 p))
             (set! i (add1 i)))
           (begin
             (set! p 0)
             (set! i 1)
             (let loop-regime ()
               (when (and (< y 1) (<= i nbits))
                 (set! y (* y useed))
                 (set! i (add1 i))
                 (loop-regime)))
             (if (>= i nbits)
                 (begin
                   (set! p 2)
                   (set! i (add1 nbits)))
                 (begin
                   (set! p 1)
                   (set! i (add1 i))))))
       ;; Exponent-extraktion
       (let loop-exp ()
         (when (and (> e-val 0.5) (<= i nbits))
           (set! p (* 2 p))
           (when (>= y (expt 2 e-val))
             (set! y (/ y (expt 2 e-val)))
             (set! p (add1 p)))
           (set! e-val (/ e-val 2))
           (set! i (add1 i))
           (loop-exp)))
       (set! y (- y 1))
       ;; Fraction-bits
       (let loop-frac ()
         (when (and (> y 0) (<= i nbits))
           (set! y (* 2 y))
           (set! p (+ (* 2 p) (floor y)))
           (set! y (- y (floor y)))
           (set! i (add1 i))
           (loop-frac)))
       ;; Skift och rounding (nearest, tie to even)
       (set! p (* p (expt 2 (- (+ nbits 1) i))))
       (set! i (add1 i))
       (let* ([round-bit (bitwise-and p 1)])
         (set! p (floor (/ p 2)))
         (set! p (cond
                   [(= round-bit 0) p]
                   [(= y 0) (+ p (bitwise-and p 1))]  ;; tie to even
                   [else (add1 p)])))
       ;; Negativt tal: 2's complement (npat - p)
       (set! p (modulo (if (< x 0) (- npat p) p) npat))
       p)]))

(define (posit->number p n es)
  ;; Full decode enligt Gustafson/Unum Type III
  ;; Returnerar exakt reellt tal (flonum för enkelhet, men kan utökas till rational).
  (cond
    [(= p 0) 0]
    [(= p (arithmetic-shift 1 (- n 1))) +inf.0]  ;; NaR / ∞
    [else
     (let* ([sign (if (zero? (bitwise-and p (arithmetic-shift 1 (- n 1)))) 1 -1)]
            [abs-p (if (= sign 1)
                       p
                       (bitwise-and (+ (bitwise-not p) 1) (- (expt 2 n) 1)))]
            [useed (expt 2 (expt 2 es))])
       ;; Extrahera regime (runlength m av identiska bits)
       (let*-values ([(r m) (let ([first-r (bitwise-and (arithmetic-shift abs-p (- (- n 2))) 1)])
                              (let loop ([pos (- n 2)]
                                         [m 0])
                                (if (or (< pos -1) (> m n))
                                    (values first-r m)
                                    (let ([bit (bitwise-and (arithmetic-shift abs-p (- pos)) 1)])
                                      (if (= bit first-r)
                                          (loop (sub1 pos) (add1 m))
                                          (values first-r m))))))])
         (let* ([k (if (= r 0) (- m) (- m 1))]
                ;; Regime-field tar m + 1 bits (run + terminator)
                [regime-bits-used (+ m 1)]
                [exp-start-pos (- (- n 2) regime-bits-used)]  ;; första exp-bit (MSB)
                ;; Ta upp till es exp-bits (pad low bits med 0 om färre)
                [exp-bits (if (< exp-start-pos 0)
                              0
                              (let loop ([pos exp-start-pos]
                                         [bits-left es]
                                         [e-acc 0])
                                (if (or (<= bits-left 0) (< pos -1))
                                    e-acc
                                    (let ([bit (bitwise-and (arithmetic-shift abs-p (- pos)) 1)])
                                      (loop (sub1 pos)
                                            (sub1 bits-left)
                                            (+ (* e-acc 2) bit))))))]
                [exp-used (min es (max 0 (+ (- n 1) exp-start-pos)))]  ;; tillgängliga bits
                [f-start-pos (- exp-start-pos exp-used)]
                ;; Fraction (remaining bits, hidden 1.)
                [f (if (< f-start-pos 0)
                       0
                       (let loop ([pos f-start-pos]
                                  [f-acc 0]
                                  [fsize 0])
                         (if (< pos -1)
                             f-acc
                             (let ([bit (bitwise-and (arithmetic-shift abs-p (- pos)) 1)])
                               (loop (sub1 pos)
                                     (+ (* f-acc 2) bit)
                                     (add1 fsize))))))]
                [fsize (max 0 (- n 1 regime-bits-used exp-used))]  ;; antal fraction-bits
                [significand (+ 1 (/ f (expt 2 fsize)))])
           (* sign (expt useed k) (expt 2 exp-bits) significand))))]))

;; Aritmetik (enkel via realtal + rounding till posit)
(define (posit+ p1 p2 n es)
  (number->posit (+ (posit->number p1 n es) (posit->number p2 n es)) n es))

(define (posit- p1 p2 n es)
  (number->posit (- (posit->number p1 n es) (posit->number p2 n es)) n es))

(define (posit* p1 p2 n es)
  (number->posit (* (posit->number p1 n es) (posit->number p2 n es)) n es))

(define (posit/ p1 p2 n es)
  (number->posit (/ (posit->number p1 n es) (posit->number p2 n es)) n es))

;; Exempel på användning (kommentera bort eller kör i REPL):
;; (define n 16)
;; (define es 1)
;; (define a (number->posit 3.14 n es))   ;; -> posit för 3.14
;; (define b (number->posit 2.0 n es))
;; (posit+ a b n es)                      ;; addition
;; (posit->number a n es)                 ;; tillbaka till tal