IEEE 単精度浮動小数点数 の計算

技術士試験の問題によく IEEE 単精度浮動小数点数の計算問題が出てくるので、
Scheme で実装してみました。

情報工学部門の技術士を目指すなら、毎回計算してないでプログラム書いて解けよ!
と思いますよね?

でも試験は頭で解かないとダメですーwww
面倒くさいなもうwwwwwwwww

(define nil '())

(define (digit->integer c)
  (- (char->integer c) (char->integer #\0)))

;;

(define (number->bns num)
  (format #f "~32,'0b" num))

(define (bns->float bns)
  (let ((s (get-s bns))
        (e (get-e bns))
        (m (get-m bns)))
    (* s m (expt 2 (- e 127)))))

(define (get-s bns)
  (let ((val (string->number (substring bns 0 1) 2)))
    (if (= val 0)
        1
        -1)))

(define (get-e bns)
  (string->number (substring bns 1 9) 2))

(define (get-m bns)
  (bns->binary (substring bns 9 32)))

(define (bns->binary bns)
  (define (iter lst k val)
    (if (null? lst)
        val
        (iter (cdr lst)
              (/ k 2)
              (if (= (car lst) 1) (+ val k) val))))
  (iter (map digit->integer (string->list bns)) 0.5 1))

(define (optimize-hex f)
  (let* ((v (if (< f 0) (* -1 f) f))
         (s (string-split (number->string v) "."))
         (i (integer-part (car s)))
         (d (decimal-part (cdr s)))
         (e (- (string-length i) 1))
         (m (substring (string-append (substring i 1 (+ e 1)) d) 0 23)))
    (cons (+ 127 e) m)))

(define (integer-part str)
  (format #f "~b" (string->number str)))

(define (decimal-part str)
  (if (null? str)
      (number->bns 0)
      (str->bns (car str))))

(define (str->bns str)
  (define (iter i k v bns)
    (if (> i 23)
        bns
        (if (>= v k)
            (iter (+ i 1)
                  (/ k 2)
                  (- v k)
                  (string-append bns "1"))
            (iter (+ i 1)
                  (/ k 2)
                  v
                  (string-append bns "0")))))
  (iter 1 0.5 (string->number (string-append "0." str)) ""))

(define (bns->hex bns)
  (define (iter lst hex)
    (if (null? lst)
        hex
        (iter (cddddr lst)
              (string-append
               hex
               (format #f "~x"
                       (+ (* 8 (car lst))
                          (* 4 (cadr lst))
                          (* 2 (caddr lst))
                          (cadddr lst)))))))
  (iter (map digit->integer (string->list bns)) ""))

;; IEEE 標準形式(単精度) 16->2 進数変換
(define (hex->float hex)
  (bns->float (number->bns hex)))

;; IEEE 標準形式(単精度) 2->16 進数変換
(define (float->hex f)
  (let* ((s (if (< f 0) "1" "0"))
         (h (optimize-hex f))
         (e (car h))
         (m (cdr h)))
    (bns->hex
     (string-append s
                    (format #f "~8,'0b" e)
                    m))))

難しい事は何もやってません。計算してみます。

gosh> (float->hex (* (hex->float #x40a80000) (hex->float #xc0300000)))
"c1670000"

結果が文字列になっていて Scheme の 16 進表現と違いますが・・・

漢は細かいこと気にしない!w 計算結果が合っていれば良いのです。(手抜き