如何使用双浮?

时间:2014-01-25 16:56:23

标签: lisp common-lisp

我正在努力想弄清楚如何告诉Lisp我想使用double-float值。假设我有:

(let ((x 1)) (format t "~A~%" (/ x 3.0)))

给出了:

0.33333334

如果我想使用double-float,我试过这个:

(let ((x 1)) (declare (type double-float x)) (format t "~A~%" (/ x 3.0)))
0.33333334

所以结果不是双浮动。但是,我可以像这样强制双浮:

(let ((x 1)) (format t "~A~%" (/ x 3.0d0)))
0.3333333333333333d0

现在我得到了双浮动结果。

所以我的问题是:如果我在定义一个表单或函数,我希望算术在双浮点中,我该如何建立?我已经阅读了大量有关使用declareproclaim等的在线资源,但无法将其用于获取我之后的结果。我不相信我知道如何在这种情况下使用它们,或者即使它们是正确使用的机制。

如果我尝试执行long-float或其他非默认设置,则同样的问题也适用。

2 个答案:

答案 0 :(得分:8)

如果您想使用特殊的浮动格式进行计算,则必须告诉它。通常如果你划分双浮点数,结果将是双浮点数。如果你有常量,你需要表示它们。

Common Lisp标准说:数值函数的结果是函数的所有浮点参数中最大格式的浮点数。

以下解释取决于一些事项。这取决于读者如何读取数字。对于整数,可以指定基数。对于浮点数,它取决于默认的浮点格式。

(let ((x 1)) (format t "~A~%" (/ x 3.0)))

让我们看看*read-default-float-format*如何影响它:

CL-USER 9 > *read-default-float-format*
SINGLE-FLOAT

CL-USER 10 > (let ((x 1)) (format t "~A~%" (/ x 3.0)))
0.33333334
NIL

CL-USER 11 > (setf *read-default-float-format* 'double-float)
DOUBLE-FLOAT

CL-USER 12 > (let ((x 1)) (format t "~A~%" (/ x 3.0)))
0.3333333333333333
NIL

另请注意,您可以使用exponent marker

指定文字数字的类型
  • d = double-float
  • e = *read-default-float-format*
  • 的浮点数
  • f = single-float
  • l = long-float
  • s = short-float

示例:

CL-USER 15 > (setf *read-default-float-format* 'single-float)
SINGLE-FLOAT

CL-USER 16 > (let ((x 1)) (format t "~A~%" (/ x 3.0d0)))
0.3333333333333333D0
NIL

您还可以号码强制转换为某种类型。函数COERCE使您明确指出了哪种类型:

CL-USER 17 > (let ((x 1))
               (format t "~A~%" (/ (coerce x 'double-float) 3.0)))
0.3333333333333333D0
NIL

答案 1 :(得分:3)

如您所述,您可以在号码后面输入d0。例如,

* 3.0d0
; => 3.0d0
* (type-of 3.0d0)
;=> DOUBLE-FLOAT

但是,这是双浮点的文字符号,就像1是整数的文字符号一样。您可以使用*read-default-float-format*Rainer Joswig's answer shows how自定义来自阅读器的默认浮点数类型。声明

(declare (type double-float x))

是对编译器的承诺,即变量x的值是双浮点数。你骗了编译器。要获得双浮点数,您需要将一个写为文字(例如1.0d0)或使用float函数转换一个:

* (float 1 0.0d0)
;=> 1.0d0

你也可以在这里使用coerce

* (coerce 1 'double-float)
;=> 1.0d0

当有选项时,比较它们是合理的。在SBCL中,事实证明这两个选项实际上编译为同一个东西:

CL-USER> (disassemble (compile nil (lambda (x) (coerce x 'double-float))))
; disassembly for (LAMBDA (X))
; 039C33E8:       488BD6           MOV RDX, RSI               ; no-arg-parsing entry point
;      3EB:       488B059EFFFFFF   MOV RAX, [RIP-98]          ; #<FDEFINITION object for SB-KERNEL:%DOUBLE-FLOAT>
;      3F2:       B908000000       MOV ECX, 8
;      3F7:       FF7508           PUSH QWORD PTR [RBP+8]
;      3FA:       FF6009           JMP QWORD PTR [RAX+9]
;      3FD:       CC0A             BREAK 10                   ; error trap
;      3FF:       02               BYTE #X02
;      400:       18               BYTE #X18                  ; INVALID-ARG-COUNT-ERROR
;      401:       54               BYTE #X54                  ; RCX
NIL
CL-USER> (disassemble (compile nil (lambda (x) (float x 0.0d0))))
; disassembly for (LAMBDA (X))
; 03BC5B18:       488BD6           MOV RDX, RSI               ; no-arg-parsing entry point
;       1B:       488B059EFFFFFF   MOV RAX, [RIP-98]          ; #<FDEFINITION object for SB-KERNEL:%DOUBLE-FLOAT>
;       22:       B908000000       MOV ECX, 8
;       27:       FF7508           PUSH QWORD PTR [RBP+8]
;       2A:       FF6009           JMP QWORD PTR [RAX+9]
;       2D:       CC0A             BREAK 10                   ; error trap
;       2F:       02               BYTE #X02
;       30:       18               BYTE #X18                  ; INVALID-ARG-COUNT-ERROR
;       31:       54               BYTE #X54                  ; RCX
NIL

听起来你正试图做一些自动转换,我认为你不会想办法做到这一点。如果您有一个号码,并且您想要double-float,则必须自行转换。如果你想检查,进来的值是double-float,你可能对声明有好运,但是类型声明只是对编译器的承诺什么东西会有特定的类型;这通常意味着编译器可以省略检查,因为已经承诺该值将具有某种类型。那就是说,你可能会有运气;在SBCL:

> (defun foo (x)
    (declare (double-float x))
    (+ x 2.0d0))

> (foo 3)
; The value 3 is not of type DOUBLE-FLOAT.
;    [Condition of type TYPE-ERROR]

但是,如果更改安全优化,可能会得到不同的结果。如果您想确保进行类型检查,请使用check-type

> (defun foo (x)
    (check-type x double-float)
    (+ x 2.0d0))

> (foo 3)
; The value of X is 3, which is not of type DOUBLE-FLOAT.
;    [Condition of type SIMPLE-TYPE-ERROR]