Как использовать 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]