好吧,编译器正在告诉你答案,也许是以一种有点无益的方式。如果您有两个修复编号,则情况并非如此,例如,将它们相加会产生修复编号:类型fixnum
在算术运算下不封闭(甚至在+
, -
and *
, 不考虑/
).
来自SBCL手册 http://www.sbcl.org/manual/#Declarations-as-Assertions:
SBCL 编译器处理类型声明的方式与大多数其他 Lisp 编译器不同。在默认编译策略下,编译器不会盲目相信类型声明,而是认为它们是关于应该检查的程序的断言:所有尚未被证明始终有效的类型声明都会在运行时断言。
如果你想编译机器算术,你需要做的是告诉编译器它正在使用的类型足够好,它可以知道结果类型足够好,可以立即表示。
给定函数中的算术,并假设 64 位实现,那么一个好的类型是(signed-byte 31)
: 使用起来很诱人(signed-byte 32)
但这失败了,因为你最终得到的东西比(signed-byte 64)
.
因此,除了在返回时使用最终的双浮点数之外,此代码不会发出警告:
(deftype smallish-integer (&optional (bits 31))
`(signed-byte ,bits))
(declaim (ftype (function (smallish-integer smallish-integer) double-float)
fixnumtest)
(inline fixnumtest))
(defun fixnumtest (i j)
(declare (optimize (speed 2)))
(declare (type smallish-integer i j))
(let* ((n (+ i j))
(n+1 (1+ n)))
(/ 1.0d0 (* n n+1))))
值得注意的是,一个(signed-byte 64)
比 a 大很多fixnum
:这没关系,因为在函数内,编译器可以处理适合寄存器的数字,即使它们比fixnums大。
我对 x64 汇编器不太熟悉,无法检查所有算术是否都编译为机器指令,但看起来确实如此。
也许可以说服 SBCL 编译器,让其相信您并不关心获得正确答案,它应该只执行机器算术,即使它知道可能会溢出。我不知道该怎么做。