我正在努力想弄清楚如何告诉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
现在我得到了双浮动结果。
所以我的问题是:如果我在定义一个表单或函数,我希望算术在双浮点中,我该如何建立?我已经阅读了大量有关使用declare
,proclaim
等的在线资源,但无法将其用于获取我之后的结果。我不相信我知道如何在这种情况下使用它们,或者即使它们是正确使用的机制。
如果我尝试执行long-float
或其他非默认设置,则同样的问题也适用。
答案 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:
指定文字数字的类型*read-default-float-format*
示例:
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]