sbcl 在将有理数转换为十进制表示法时尽可能保持精度

sbcl keep as much precision as possible when converting rational to decimal notation

我这里有一个简单的 lisp 程序,它计算在单位间隔上随机均匀选择的两个点之间的平均长度的近似值。如果我 运行 程序,我得到一个有理数 16666671666667/50000000000000,但是当我(天真地)尝试将有理数格式化为 20 位时,一些精度被丢弃 0.33333343000000000000。我认为,在幕后,SBCL 在格式化之前将有理数转换为浮点数,但我不太确定如何分辨。我只是使用表达式 (format t "~20$~%" (scale-all-line-contributions 10000000 1)。有没有办法将有理数转换为十进制表示法,并尽可能保持精度?我了解格式系统功能强大且范围广泛,但我无法找到专门与有理数相关的文档。

为了完整起见,下面是代码,因为它不是很长。

(defun number-of-pairs (n i)
  "get the number of pairs with distance x
  for non-zero distances we have to consider two cases"
  (cond
    ((= i 0) n)
    ((> i 0) (* 2 (- n i)))
    ((> i n) 0)))

(defun line-contribution (n i power)
  "get the number of segments of length i in a line of n segments and their weight combined"
  (let
    ((number-of-pairs (number-of-pairs n i))
     (weight-of-pair (expt i power)))
    (* number-of-pairs weight-of-pair)))

(defun all-line-contributions (n power)
  "get the line contributions for reach [0 .. n]"
  (loop for i from 1 upto (- n 1) summing (line-contribution n i power)))

(defun normalized-all-line-contributions (n power)
  "normalize line contributions by number of pairs"
  (let ((pair-count (expt n 2)))
    (/ (all-line-contributions n power) pair-count)))

(defun scale-all-line-contributions (n power)
  "scale the line contributions by the distance n
  this will guarantee convergence"
  (/ (normalized-all-line-contributions n power) (expt n power)))

(print (scale-all-line-contributions 10000000 1))
(format t "~20$~%" (scale-all-line-contributions 10000000 1))

编辑:修复了代码中的逻辑错误。新的有理数,浮点对是33333333333333/100000000000000 0.33333334000000000000

您可以使用 coerce or float。例如:

(format t "~20$" (coerce 16666671666667/50000000000000 'long-float))

; prints 0.33333343333334000000

(format t "~a" (float 16666671666667/50000000000000 1.0l0))

; prints 0.33333343333334d0

请注意,对 long-float 的强制转换会在 Common Lisp 的不同实现中产生不同的结果(特别是在 CLISP 中)。

float 的第二个参数是一个原型:您应该提供任何浮点数,第一个参数将被转换为相同类型的 float