I am struggling a little trying to figure out how to tell Lisp that I want to use double-float values. Suppose I have:
(let ((x 1)) (format t "~A~%" (/ x 3.0)))
Which gives:
0.33333334
If I want to use double-float, I tried this:
(let ((x 1)) (declare (type double-float x)) (format t "~A~%" (/ x 3.0)))
0.33333334
So the result is not a double-float. I can, however, force double-float like this:
(let ((x 1)) (format t "~A~%" (/ x 3.0d0)))
0.3333333333333333d0
And now I get a double-float result.
So my question is: if I'm defining a form or function in which I want arithmetic to be in double-float, how do I establish that? I've read lots of online resources on using declare
, proclaim
, etc, but haven't been able to apply it to getting the result I'm after. I'm not convinced I know how to utilize these in this context, or even if they are the correct mechanism to use.
Same question would apply if I were trying to do long-float
or anything else that isn't the default.
Double is more precise than float and can store 64 bits, double of the number of bits float can store. Double is more precise and for storing large numbers, we prefer double over float. For example, to store the annual salary of the CEO of a company, double will be a more accurate choice.
To answer your question, you can add a float to a double and vice versa. Generally, the result will be made into a double , and you will have to cast it back to a float if that is what you want.
A float has 7 decimal digits of precision and occupies 32 bits . A double is a 64-bit IEEE 754 double-precision floating-point number. 1 bit for the sign, 11 bits for the exponent, and 52 bits for the value. A double has 15 decimal digits of precision and occupies a total of 64 bits .
Use double for all your calculations and temp variables. Use float when you need to maintain an array of numbers - float[] (if precision is sufficient), and you are dealing with over tens of thousands of float numbers.
If you want to compute with a special float format you have to tell it. Usually if you divide double-floats, the result will be a double float. If you have constants, you need to denote them as such.
The Common Lisp standard says: The result of a numerical function is a float of the largest format among all the floating-point arguments to the function..
The interpretation of the following depends on a few things. It depends on how the reader reads numbers. For integers the base can be specified. For floats it depends on the default float format.
(let ((x 1)) (format t "~A~%" (/ x 3.0)))
Let's see how *read-default-float-format*
affects it:
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
Also note that you can specify the type for literal numbers by using an exponent marker:
*read-default-float-format*
Example:
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
You can also coerce numbers to a certain type. The function COERCE
makes it explicit which type you mean:
CL-USER 17 > (let ((x 1))
(format t "~A~%" (/ (coerce x 'double-float) 3.0)))
0.3333333333333333D0
NIL
As you noted you can type d0
after the number. E.g.,
* 3.0d0
; => 3.0d0
* (type-of 3.0d0)
;=> DOUBLE-FLOAT
However, that's the literal notation for a double float, just like 1
is the literal notation for an integer. You can customize the default type of floating point numbers from the reader with *read-default-float-format*
, and Rainer Joswig's answer shows how. The declaration
(declare (type double-float x))
is a promise to the compiler that the value of the variable x
is a double float. You're lying to the compiler. To get a double float, you'll need to either write one as a literal (e.g., 1.0d0
) or convert one with the float
function:
* (float 1 0.0d0)
;=> 1.0d0
You could also use coerce
here:
* (coerce 1 'double-float)
;=> 1.0d0
When there's an option, it's reasonable to compare them. In SBCL, it turns out that these two options actually compile to the same thing:
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
It sounds like you're trying to do some automatic conversion, and I don't think you're going to find a way to do that. If you've got a number coming in, and you want a double-float
you'll have to convert it yourself. If you want to check that a value coming in is a double-float
you might have some luck with declarations, but a type declaration is just a promise to the compiler that something will have a particular type; this usually means that the compiler can omit checks since it's been promised that the value will have a certain type. That said, you might have some luck; in 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]
You might get different results if you change the safety optimization, though. If you want to ensure a type check, then use 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]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With