読者です 読者をやめる 読者になる 読者になる

数値の差を表示する elisp 書いてみた

elisp Emacs

ある数値(まぁ為替レートなんだけど)の差分を計算してよしなに表示できたらいいなーという思いが頭をもたげてきたので elisp を書いてみた。

113.750 と 113.855 の差分は普通に引き算すると 0.105 なんだけど諸般の事情により 10.5 と表示したい。同様に 1.09321 と 1.09301 の差分は 0.00020 ではなく 2.0 と表示したい。小数点以下の桁数が 3 桁か 5 桁かによって何倍するかが異なる、ということ。

最初に書いたのはこんな感じ。

;; 小数点以下が 5 桁かどうか判定する
(defun fivedigits (x)
  (if (< (* x 1000) 10000)
      t
    nil))

(defun my-round (value)
  ;; 指定した桁数以下を捨てる
  (defun my-slice (value digit)
    (setq str_value
          (substring (number-to-string value) 0 digit)
          )
    (string-to-number str_value))
  (cond ((> -99.9 value)
         (my-slice value 6))

        ((> -9.9 value)
         (my-slice value 5))

        ((> 0 value)
         (my-slice value 4))

        ((< 99.9 value)
         (my-slice value 5))

        ((< 9.9 value)
         (my-slice value 4))

        ((< 0 value)
         (my-slice value 3))
        )
  )

(defun long (x y)
  (setq value
        (if (fivedigits x)
            (* 10000 (- y x))
          (* 100 (- y x))))
  (my-round value))

(defun short (x y)
  (setq value
        (if (fivedigits x)
            (* 10000 (- x y))
          (* 100 (- x y))))
  (my-round value))

この長さの lisp を書いたのは初めて。cond とか初めて使った。久しぶりに基礎文法最速マスターシリーズのエントリを読んだな。

で使ってみると、

(long 123.342 124.642)                    ; => 129.9
(long 123.343 123.393)                    ; => 4.9

(short 1.11023 1.11011)                 ; => 1.2
(short 1.11023 1.11411)                 ; => -38.7

あれ、誤差が。


そうか、最初に整数にしてから引き算していけばいいのだと気づく。

;; 小数点以下が 5 桁かどうか判定する(上と同じ)
(defun fivedigits (x)
  (if (< (* x 1000) 10000)
      t
    nil))

(defun long1 (x y)
  (if (fivedigits x)
      (/ (- (* 100000 y) (* 100000 x)) 10)
    (/ (- (* 1000 y) (* 1000 x)) 10)))

(defun short1 (x y)
  (if (fivedigits x)
      (/ (- (* 100000 x) (* 100000 y)) 10)
    (/ (- (* 1000 x) (* 1000 y)) 10)))

cond とか使わなくても事足りた。

(long1 123.342 124.642)                   ; => 130.0
(long1 123.343 123.393)                   ; => 5.0

(short1 1.11023 1.11011)                 ; => 1.2
(short1 1.11023 1.11411)                 ; => -38.8

見たところ合ってるっぽいので、たぶんこれでいいのではないかと思う。
最初の誤差が出るバージョンを書くのにすごく時間がかかったし、直した方はただの四則演算だけですぐに書けたし、何だか複雑な気持ち。