導入
単精度浮動小数点数を扱うプログラムで書くことで、単精度浮動小数点数の内部表現についての理解を深めます。
プログラム
(use srfi-13)
(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 (dec->num dec)
(define (iter lis val)
(if (null? lis)
val
(iter (cdr lis) (+ (* 2 val) (car lis)))))
(iter (map digit->integer (string->list dec)) 0))
(define (round-dec dec-pt)
(let ((lsd (string->number (substring dec-pt 23 24)))
(dec (substring dec-pt 0 23)))
(if (= lsd 0)
dec
(let ((rd (+ (dec->num dec) lsd)))
(if (>= rd #xFFFFFF)
dec
(format #f "~23,'0b" rd))))))
(define (decimal->bin d k bin)
(if (or (> k 24) (= d 0))
bin
(let* ((s (string-split (number->string (* d 2)) "."))
(i (car s))
(nd (cadr s)))
(decimal->bin (string->number (string-append "0." nd)) (+ k 1) (string-append bin i)))))
(define (integer-part str)
(format #f "~b" (string->number str)))
(define (decimal-part str)
(decimal->bin str 0 ""))
(define (optimize-hex f)
(define (opt-int int-pt dec-pt)
(cons
(+ 127 (- (string-length int-pt) 1))
(round-dec (string-pad-right (string-append (substring int-pt 1 (string-length int-pt)) dec-pt) 24 #\0))))
(define (opt-dec dec-pt)
(define (iter d k)
(if (or (null? d) (= (car d) 1))
(cons (- 127 k)
(round-dec (string-pad-right (substring dec-pt k (string-length dec-pt)) 24 #\0)))
(iter (cdr d) (+ k 1))))
(iter (map digit->integer (string->list dec-pt)) 1))
(let* ((v (if (< f 0) (* -1 f) f))
(s (string-split (number->string v) "."))
(i (car s))
(d (string->number (string-append "0." (cadr s)))))
(let* ((int-pt (integer-part i))
(dec-pt (decimal-part d)))
(if (> (string->number int-pt) 0)
(opt-int int-pt dec-pt)
(opt-dec dec-pt)))))
(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)
(inexact (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))))
実行例
1.
gosh> (float->hex (* (hex->float #x40a80000) (hex->float #xc0300000)))
"c1670000"
2.
gosh> (+ (hex->float #xC12C0000) (hex->float #x40A40000))
-5.625
参考文献
実行例1 ... 技術士一次試験 平成27年度 専門科目 III-32
実行例2 ... 技術士一次試験 平成23年度 専門科目 IV-8