Как использовать double-float?
Я изо всех сил пытаюсь понять, как сказать Lisp, что я хочу использовать значения с двойным поплавком. Предположим, я:
(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
и теперь я получаю результат двойного поплавка.
Итак, мой вопрос: если я определяю форму или функцию, в которой я хотите, чтобы арифметика была в double-float, как это установить? Я прочитал много интернет-ресурсов с помощью declare
, proclaim
и т. д., Но не смогли применить его для получения результата я после. Я не уверен, что знаю, как использовать их в этом контексте, или даже если они являются правильным механизмом для использования.
тот же вопрос будет применяться, если я пытаюсь сделать long-float
или что-нибудь еще, что не по умолчанию.
2 ответов
если вы хотите вычислить со специальным форматом float, вы должны сказать это. Обычно, если вы разделяете двойные поплавки, результатом будет двойной поплавок. Если у вас есть константы, вам нужно обозначить их как таковые.
общий стандарт Lisp говорит:результатом числовой функции является поплавок наибольшего формата среди всех аргументов с плавающей запятой функции..
интерпретации зависит от нескольких вещей. Это зависит от того, как читатель читает цифры. Для целых чисел можно указать базу. Для поплавков это зависит от формата float по умолчанию.
(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
также обратите внимание, что вы можете указать тип для литеральных чисел с помощью показатель маркер:
- d = двойной поплавок
- e = поплавок
*read-default-float-format*
- f = single-float
- l = long-float
- s = короткий-поплавок
пример:
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
как вы отметили, Вы можете ввести d0
после номера. Е. Г.,
* 3.0d0
; => 3.0d0
* (type-of 3.0d0)
;=> DOUBLE-FLOAT
тем не менее, это буквальное обозначение для двойного поплавка, как 1
- буквенное обозначение целого числа. Вы можете настроить тип чисел с плавающей запятой по умолчанию из считывателя с помощью *read-default-float-format*
и Райнер Joswig показывает, как. Декларация
(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]