Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use double-float?

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.

like image 578
lurker Avatar asked Jan 25 '14 16:01

lurker


People also ask

How do you use double and float?

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.

Can you put a double in a float?

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.

What is float double?

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 .

Why would you use float over double?

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.


2 Answers

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:

  • d = double-float
  • e = float of *read-default-float-format*
  • f = single-float
  • l = long-float
  • s = short-float

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
like image 106
Rainer Joswig Avatar answered Oct 08 '22 16:10

Rainer Joswig


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]
like image 3
Joshua Taylor Avatar answered Oct 08 '22 18:10

Joshua Taylor