calc of emacs
Table of Contents
高精度
前回はpiの精度についてNASAまで行ってその、制度を確認してみた。こういう 数学的な数字は幾らでも精密に求められる。翻って物理の世界はどうなんだろ う。長さを、とことん測定するとか、灯油18リットル頼んだのに、17.9833リッ トルしか入れてくれなかったりとかの、切実な問題がある。
Q: 物理量で精度よく(桁数が多く)測定できるものを5種類列挙し、その精度も説 明してください。個人的には、時間(周波数)が一番と、思っています。
A: 時間や周波数は確かに非常に高い精度で測定できる物理量です。他にも精度の高い測定が可能な物理量はいくつかあります。
- 周波数(時間): 周波数は非常に高い精度で測定できます。原子時計などの装置を使用して、非常に正確な時間測定が可能です。原子時計は原子の振動や放射線の周波数を基準として使用し、極めて正確な時間を示します。例えば、セシウム原子時計は1億年にわたって1秒の誤差が僅かです。
- 電気電流: 電流の測定も非常に高い精度で行えます。ナノアンペアやピコアンペアなど微小な電流を測定することができます。これは、微小な電流を検出する高感度な機器や測定器を用いることで実現されます。
- 質量: 質量も高い精度で測定可能です。質量の測定には、電磁バランスや超微量天秤などの高精度な装置が使われ、微小な質量の変化も検出できます。
- 周波数(波長): 光の周波数や波長も高い精度で測定可能です。レーザー干渉計や分光器などの装置を使用して、光の波長や周波数を極めて正確に測定できます。
- 温度: 温度の測定も高い精度で行えます。絶対零度に近い超低温から極めて高温の状態まで、厳密な温度制御と高精度の温度計を使用することで、温度を非常に正確に測定できます。
これらの物理量は、それぞれ高精度な測定器や装置を用いることで、非常に正確な測定が可能となります。
Q: 2.の電気電流は、微小と言うだけで、1.23e-9 [A] = 1.23[nA] を言ってるだ けだよね。1.23456789e-9 の様に仮数部の桁も大きいって事? なにか、とり 違えてませんか?
A: すみません、誤解を招く表現でした。電気電流の測定は、単に微小な電流を測定するだけではなく、仮数部の桁も含めた精度の高い測定が可能です。1.23e-9 [A]と1.23456789e-9のように、仮数部の桁数が多い値も正確に測定できます。
電流の測定器や機器は、超微細な電流を高い感度で検出し、その電流値を高い精度で読み取ることができます。そのため、仮数部の桁数が大きい微小な電流でも、正確に測定できるようになっています。
Q: 具体的には、仮数部は何桁まで、測定できているのでしょうか。実現方法も解 説してください。
A: 電気電流の測定において、仮数部の桁数がどれくらいまで測定可能かは、使用される測定器や装置の性能や精度に依存します。一般的に、最先端の高性能な測定器を使用すると、仮数部の桁数は非常に大きくなりますが、それぞれの装置によって異なります。
例えば、ナノアンペアやピコアンペアのオーダーでの電流を測定する場合、近年の高性能な測定器では仮数部が8桁から10桁以上の精度で測定できることがあります。つまり、1.23456789e-9のような桁数まで測定可能です。
これを実現するためには、高感度な増幅器や高精度なADC(アナログ-デジタル変換器)などの装置を使用し、微小な電流を検出し、それをデジタル信号に変換する際に情報を可能な限り失わないようにします。さらに、ノイズや外部の干渉を最小限に抑えるために、シールドやノイズフィルターなども使用されます。
ただし、これらの数字は一般的なものであり、実際の測定器の仕様や製造元によって異なる場合があります。そのため、最新の高性能な測定器を用いて実際の仮数部の桁数を特定するには、各測定器の技術仕様書や詳細な性能評価が必要です。
次は、時間についての質問だな。きっと、この方が登場する事でしょう。 新たな“秒の定義”になるか…ノーベル賞候補、香取東大教授が語る「光格子時計」の世界
defmath結果を展開したい
これまた、納豆のように前回の記事の糸を引く取り組みでdefmathの続き。 こいつが吐き出すソースを眺めてみたい。そんなの無理筋って事で、諦めてい たんだけどね。
でもマクロなら、展開したコードを実行って流れのはず。だったら、展開した コードをリストにしちゃったらどうだろう? そんな事で、defmathをちょっと 改変してみた。単に math-do-defmath をlistで包んだだけ。
(defmacro defmath-expand (func args &rest body) ; [Public] (declare (doc-string 3) (indent defun)) ;; FIXME: Edebug spec? (require 'calc-ext) (list (math-do-defmath func args body))) (defmath-expand myfact (n) (if (> n 0) (* n (myfact (1- n))) 1))
試供品もハロワを使ってみる。
*** Eval error *** Invalid function: (progn nil (defun calcFunc-myfact (n) (if (math-posp n) (math-mul n (calcFunc-myfact (math-add n -1)))
見事に怒られた。でも、展開結果がチラ見できてるよね。結果は細密コードに なってるけど。ゆくゆくは清書したいな。そこで、天下のAI様にお願いしてみ る。
ChatGPTは美的 ?
なんでも、OpenAIで偽写真やら動画を作成するが流行してる。時々、指が6本 の写真とかが作成されちゃうみたいで、これを防止するアプリが発表されてる とか。AIの美的なセンスで、こうなってしまうのだろうか? いやAIは宇宙人 の地球侵略装置なんで、ついつい地が出たのさ。宇宙人は6本指。
この6本指は、はたから見て偽ってもすぐに見破る事が出来るので、これを逆手 に取る悪い人がいるらしい。なにか悪さをする時、フェイクの6本指手袋とか で、事を行なう。そうすれば、監視カメラに捉えられても、それは偽動画で しょって、言い逃れできる。
オイラーはそこまで悪じゃないんで、正統なAI利用を目指す。うえの細密なS 式を、清書してねってお願いだ。
Q: 次のLispの式を清書(pretty-print)してください。 (progn nil (defun calcFunc-myfact (n) (if (math-posp n) (math-mul n (calcFunc-myfact (math-add n -1)))
A: すみません、Lispの式の閉じカッコが足りないようですね。正しい形にしてみますね。
(progn nil (defun calcFunc-myfact (n) (if (math-posp n) (math-mul n (calcFunc-myfact (math-add n -1))) ; ここに何か返す値を指定する必要があります ) ) )
このコードは、`calcFunc-myfact`という関数を定義していますが、条件文の中で再帰的に自分自身を呼び出しています。ただし、再帰のベースケース(再帰を終了させる条件)が不足しているようです。再帰関数では、特定の条件で再帰を終了する箇所が必要です。その条件を追加してコードを完成させてください。
それなりに美的になった。おまけで教育的な指導も入っているよ。
defmath-pp
エラーになる所を、何とか回避できたとして、清書をAIにお願いするのは、癪 にさわる。emacs内の事はemacs中で解決したいぞ。それらしい関数を探してみ た。
(defmacro defmath-pp (func args &rest body) ; [Public] (declare (doc-string 3) (indent defun)) ;; FIXME: Edebug spec? (require 'calc-ext) (pp (math-do-defmath func args body)))
そしたら、29.1/lisp/emacs-lisp/pp.elなんてのを発見。pp-xxxってのが多数 用意されてたけど、初心者なので、素直に親玉っぽい、ppで包んでみる。
(defmath-pp mysin ((float (anglep x))) (interactive 1 "mysn") (setq x (to-radians x)) ; Convert from current angular mode. (let ((sum x) ; Initial term of Taylor expansion of sin. newsum (nfact 1) ; "nfact" equals "n" factorial at all times. (xnegsqr :"-(x^2)")) ; "xnegsqr" equals -x^2. (for ((n 3 100 2)) ; Upper limit of 100 is a good precaution. (working "mysin" sum) ; Display "Working" message, if enabled. (setq nfact (* nfact (1- n) n) x (* x xnegsqr) newsum (+ sum (/ x nfact))) (if (~= newsum sum) ; If newsum is "nearly equal to" sum, (break)) ; then we are done. (setq sum newsum)) sum))
そして強気に、試供品は、mysin。これでどうや?
"(progn (defun calc-mysin nil (interactive) (calc-slow-wrapper (calc-enter-result 1 \"mysn\" (cons 'calcFunc-mysin (calc-top-list-n 1))))) (defun calcFunc-mysin (x) (or (math-anglep x) (math-reject-arg x 'anglep)) : (setq n (+ n 2))))) sum))) "
今度は成功。ちゃんと整形された文字列で答えが得られた。prognを使って2つ の関数を定義してるのね。但し清書が強烈過ぎて、とことん展開された形になっ ている。
prognを除去し、defunを最左に配置。後はオイラーの美的センスで再調整。最 後の仕上げとして、C-M-q して、emacsに再配置を指示。これをやるには、ファ イル名が、*.el になってる必要が有る。
(defun calc-mysin nil (interactive) (calc-slow-wrapper (calc-enter-result 1 "mysn" (cons 'calcFunc-mysin (calc-top-list-n 1))))) (defun calcFunc-mysin (x) (or (math-anglep x) (math-reject-arg x 'anglep)) (setq x (math-check-float x)) (setq x (math-to-radians x)) (let ((sum x) newsum (nfact 1) (xnegsqr (calcFunc-neg (math-pow x 2)))) (catch 'math-break (let ((n 3)) (while (<= n 100) (if (eq calc-display-working-message 'lots) (math-do-working "mysin" sum)) (setq nfact (math-mul (math-mul nfact (math-add n -1)) n) x (math-mul x xnegsqr) newsum (math-add sum (math-div x nfact))) (if (math-nearly-equal newsum sum) (math-break)) (setq sum newsum) (setq n (+ n 2))))) sum))
一つ注意、文字列を囲んでいる " 記号がバックスラッシュでエスケープされ ているので、評価時は外しておく事。これをおこたると、エラーにもならない のに、機能しない。
defmathに記述されてる言語は、calc語の拡張版だ。例えば、for構文なんて calc語には無い。でも便利だから提供するよって方針。コンパイルすると、 whileを主としたものに展開される。ループの中からの脱出であるbreakもそう だね。emacsがeval.cで提供してる、catch関数の組み合わせに展開される。
その他では、+-*/ とかの一般に見掛ける演算子が、calcが提供してる、 math-add等に変換される。これで、任意桁の浮動小数点機構を実現してる(は ず)。コンパイラー凄いな。
なお、関数が2個定義されてる。最初の方は、キー操作で会話的に操作する為 のもの。後者は、本当の(代数的)定義になる。
精度と次数
自前のsin関数が手にはいったので改変自由。ちょいと精度とテーラー展開の次数の関係を調 べてみる。その前に小手調べ。*Message* にも記録が残るので、それを提示。
Floating-point precision is 4 digits Working... mysin = 0.4996 Floating-point precision is 10 digits Working... mysin = 0.5000000001 Floating-point precision is 20 digits Working... mysin = 0.5 Floating-point precision is 50 digits Working... mysin = 0.49999999999999999999999999999999999999999999999999 Floating-point precision is 100 digits Working... mysin = 0.4999999999999999999999999999999999999999999999999999999999\ 999999999999999999999999999999999999999998 Floating-point precision is 200 digits Working... mysin = 0.5000000000000000000000000000000000000000000000000000000000\ 0000000000000000000000000000000000000000000000000000000000000000000000000000000\ 000000000000000000000000000000000000000000000016249805444365329 Floating-point precision is 500 digits Working... mysin = 0.5000000000000000000000000000000000000000000000000000000000\ 0000000000000000000000000000000000000000000000000000000000000000000000000000000\ 0000000000000000000000000000000000000000000000162498054443653288386645340717384\ 7608863768182411737602720603307040399167471540021078346701518381806119851119294\ 1520997218480149891455719725390797084796129830219416175095506099265351678272323\ 3901986630129671375363493877644508372281549551993085950421451606215482300638534\ 36213970939679542557058177618238110370156591559
計算精度を変化させながら、それぞれでmysin(30)を計算させてみた。NSSAも びっくりの20桁以上が主戦場ね。信用できるのは100桁ぐらいまでか。
で、本題。ソース中のnの値(次数だな)を、メッセージ・バッファーに表示するように改変。
Floating-point precision is 4 digits Working... mysin = 5 Floating-point precision is 10 digits Working... mysin = 11 Floating-point precision is 20 digits Working... mysin = 19 Floating-point precision is 50 digits Working... mysin = 37 Floating-point precision is 100 digits Working... mysin = 61 Floating-point precision is 200 digits Working... mysin = 99 Floating-point precision is 500 digits Working... mysin = 99
コード中のコメントにも説明が有るように、実用的には100桁で十分との事。 あえて限界を打ち破ってこれを1000桁にしてみた。
Floating-point precision is 200 digits Working... mysin = 107 Floating-point precision is 500 digits Working... mysin = 227 Floating-point precision is 1000 digits Working... arctan = 0.197395559849880758370... if: Lisp nesting exceeds ‘max-lisp-eval-depth’: 1601
今度はemacsが耐えきれずに、根を上げたとな。
こんな風にソースが手に入いると、楽しい事がいっぱいある。
working
メッセージ・バッファーに記録を残す仕組みは、
(working "mysin" sum) ; Display "Working" message, if enabled.
元ソースのコメントに(不)親切ながら説明が出てた。マニュアルを探してみる と、こんな説明を発見。(マニュアルをブラウザーに表示させて、そこで検索 するGUI法もあるけど、オイラーはCUI派。マニュアルをtxtにしておいて、 lessで開いてから、そこで検索。正に昭和時代の古風な方法です。)
Type m w (calc-working) with a numeric prefix of 0 to disable all “working” messages. Use a numeric prefix of 1 to enable only the plain ‘Working...’ message. Use a numeric prefix of 2 to see intermediate results as well. With no numeric prefix this displays the current mode.
C-u 2 m w とかやって、設定するんだな。
要の math-add とか
任意桁の浮動小数点が、どう実現されてるか? 調べてみる。
calc.el 2487行あたりのコメント
;;;; Arithmetic routines. ;; ;; An object as manipulated by one of these routines may take any of the ;; following forms: ;; ;; integer An integer. ;; ;; (frac NUM DEN) A fraction. NUM and DEN are integers. ;; Normalized, DEN > 1. ;; ;; (float NUM EXP) A floating-point number, NUM * 10^EXP; ;; NUM and EXP are integers. ;; Normalized, NUM is not a multiple of 10, and ;; abs(NUM) < 10^calc-internal-prec. ;; Normalized zero is stored as (float 0 0). : ;; In general, combining rational numbers in a calculation always produces ;; a rational result, but if either argument is a float, result is a float. ;; In the following comments, [x y z] means result is x, args must be y, z, ;; respectively, where the code letters are: ;; ;; O Normalized object (vector or number) ;; V Normalized vector ;; N Normalized number of any type ;; N Normalized complex number ;; R Normalized real number (float or rational) ;; F Normalized floating-point number ;; T Normalized rational number ;; I Normalized integer :
こういう大切なお知らせが出てた。そして、
;;; Compute the sum of A and B. [O O O] [Public] (defun math-add (a b) (or (and (not (or (consp a) (consp b))) (+ a b)) : (and (Math-realp a) (Math-realp b) (progn (or (and (consp a) (eq (car a) 'float)) (setq a (math-float a))) (or (and (consp b) (eq (car b) 'float)) (setq b (math-float b))) (math-add-float a b)))
本当に、ただの数値(即値)なら、emacsレベルで加算してね。a,b両者がリスト で、その冒頭にfloatって印が付いていたら、浮動小数点特有の演算をやって ね。
(defun math-add-float (a b) ; [F F F] (let ((ediff (- (nth 2 a) (nth 2 b)))) (if (>= ediff 0) (if (>= ediff (+ calc-internal-prec calc-internal-prec)) a (math-make-float (math-add (nth 1 b) (if (eq ediff 0) (nth 1 a) (math-scale-left (nth 1 a) ediff))) (nth 2 b))) (if (>= (setq ediff (- ediff)) (+ calc-internal-prec calc-internal-prec)) b (math-make-float (math-add (nth 1 a) (math-scale-left (nth 1 b) ediff)) (nth 2 a))))))
長くなりそうなので、to be continue …