Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Q: ways to convert floats to integers (fixnums)

12 views
Skip to first unread message

Janis Dzerins

unread,
Dec 11, 2000, 8:23:22 AM12/11/00
to
Hello.

I'm writing a program that draws moving objects. I decided to
represent their coordinates as floats so I have a struct:

(defstuct moving-object
x y dx dy)

where x, y, dx, dy are all floats.

Drawing function needs integers so I call TRUNCATE to get an integer
but that conses like hell (50% of the space).

Another operations that conses the same way (another 50%) is
multiplication (I multiply dx and dy with delta-time).

So my question is: are there any destructive ways to convert float to
integer or do I have to resort to fixed point math?

Janis Dzerins
--
If million people say a stupid thing it's still a stupid thing.

Barry Margolin

unread,
Dec 11, 2000, 11:43:10 AM12/11/00
to
In article <873dfv1...@asaka.latnet.lv>,

Janis Dzerins <jo...@latnet.lv> wrote:
>I'm writing a program that draws moving objects. I decided to
>represent their coordinates as floats so I have a struct:
>
>(defstuct moving-object
> x y dx dy)
>
>where x, y, dx, dy are all floats.
>
>Drawing function needs integers so I call TRUNCATE to get an integer
>but that conses like hell (50% of the space).
>
>Another operations that conses the same way (another 50%) is
>multiplication (I multiply dx and dy with delta-time).
>
>So my question is: are there any destructive ways to convert float to
>integer or do I have to resort to fixed point math?

No, there's no destructive way to do this. But that's not really the
issue. The consing is happening in the calculations, not in the allocation
of the integer itself.

It's kind of surprising that these float calculations cons so much,
though. Most processors have hardware floating point units that can do
these operations in a single instruction. Perhaps the floating point type
you're using doesn't match up with the hardware's floating point precision,
i.e. you're on a machine that has hardware for single-precision floats, but
you're using double-float.

--
Barry Margolin, bar...@genuity.net
Genuity, Burlington, MA
*** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups.
Please DON'T copy followups to me -- I'll assume it wasn't posted to the group.

Erik Naggum

unread,
Dec 11, 2000, 1:47:55 PM12/11/00
to
* Janis Dzerins <jo...@latnet.lv>

| Drawing function needs integers so I call TRUNCATE to get an integer
| but that conses like hell (50% of the space).

Is that because the integers turn into bignums, i.e., they need more
than the typical 29+1 bits that fixnums are made up of?

#:Erik
--
"When you are having a bad day and it seems like everybody is trying
to piss you off, remember that it takes 42 muscles to produce a
frown, but only 4 muscles to work the trigger of a good sniper rifle."
-- Unknown

Janis Dzerins

unread,
Dec 12, 2000, 5:26:13 AM12/12/00
to
Erik Naggum <er...@naggum.net> writes:

> * Janis Dzerins <jo...@latnet.lv>
> | Drawing function needs integers so I call TRUNCATE to get an integer
> | but that conses like hell (50% of the space).
>
> Is that because the integers turn into bignums, i.e., they need more
> than the typical 29+1 bits that fixnums are made up of?

As can be seen from code below, numbers are in range from 0.0 to
400.0. From profiling results I gather that there is a lot of float
allocation going on (as opposed to consing as I said before). Here are
timing results on my machine (Intel Pentium-III, ACL 6.0 Trial):

cl-user(4): (time (animation::clx-test))
window mapped
; cpu time (non-gc) 11,570 msec user, 3,050 msec system
; cpu time (gc) 420 msec user, 0 msec system
; cpu time (total) 11,990 msec user, 3,050 msec system
; real time 15,746 msec
; space allocation:
; 226 cons cells, 117,638,416 other bytes, 0 static bytes
:quit
cl-user(5):

Although I see that those almost 120Mb other bytes are collected
immediately and have no real impact on performance, I'd like to know
ways how to avoid allocating them anyway.

Here's the code. Good suggestions are welcome.


(defpackage :animation
(:use :common-lisp :xlib))

(in-package :animation)

(eval-when (compile)
(declaim (optimize (speed 3) (safety 1) (space 0) (debug 0))))

(defvar *display* nil)
(defparameter *width* 400.0d0)
(defparameter *height* 400.0d0)

(defstruct obj
(x 0.0d0 :type double-float)
(y 0.0d0 :type double-float)
(dx 0.0d0 :type double-float)
(dy 0.0d0 :type double-float))

(defun draw-obj (obj window gc)
(draw-rectangle window gc
(1- (round (obj-x obj)))
(1- (round (obj-y obj)))
3 3 t))

(defun draw-objs (objs window gc)
(clear-area window)
(dolist (a objs)
(draw-obj a window gc)))

(defun update-obj (a dt)
(declare (type double-float dt))
(symbol-macrolet ((dx (obj-dx a))
(dy (obj-dy a))
(x (obj-x a))
(y (obj-y a)))
(let ((tx (* dx dt))
(ty (* dy dt)))
(declare (type double-float tx ty))
(setf x (+ x tx)
y (+ y ty)))
(when (>= x *width*) (setf x (- x *width*)))
(when (< x 0.0d0) (setf x (+ x *width*)))
(when (>= y *height*) (setf y (- y *height*)))
(when (< y 0.0d0) (setf y (+ y *height*)))))

(defun move-obj (obj dt window gc)
(declare (type double-float dt))
(draw-obj obj window gc)
(update-obj obj dt)
(draw-obj obj window gc))

(defun move-objs (objs dt window gc)
(declare (type double-float dt))
(dolist (a objs)
(move-obj a dt window gc))
(display-force-output *display*))

(defun clx-test ()
(let* ((*display* (open-display ""))
(screen (display-default-screen *display*))
(black (screen-black-pixel screen))
(white (screen-white-pixel screen))
(win (create-window :parent (screen-root screen)
:x 0 :y 0 :width (truncate *width*) :height (truncate *height*)
:background black
:colormap (screen-default-colormap screen)
:event-mask '(:exposure :structure-notify :button-press)))
(gc (create-gcontext :drawable win
:foreground white
:background black
:line-width 0
:line-style :solid
:cap-style :round
:function boole-xor
:font "fixed")))
(set-wm-properties win
:name "clx-test"
:resource-name "clx-test"
:resource-class "clx-test"
:command '("clx-test")
:min-width (truncate *width*)
:min-height (truncate *height*)
:input :off
:initial-state :normal)
(map-window win)
(unwind-protect
(let ((objs (make-random-objs 75))
(window-mapped nil)
(last-frame-time (get-internal-real-time))
;; This is a hack: I wrote a program that sends 1 byte every 20 milliseconds since I could not find
;; a way to sleep that amount of time -- so this program just blocks on read.
#+ignore (sock (socket:make-socket :type :stream :address-family :internet :connect :active :format :binary
:remote-host "localhost" :remote-port 2556))
)
(unwind-protect
(progn
#+ignore (read-byte sock)
(loop named foo
do (when (or (event-listen *display*)
(not window-mapped))
(event-case (*display* :discard-p t :force-output-p t)
(:exposure (window count)
(when (zerop count)
(draw-objs objs window gc))
;; wait for another event if window is not mapped,
;; otherwise exit event-case
window-mapped)
(:map-notify (window)
(when (eql window win)
(setf last-frame-time (get-internal-real-time))
(setf window-mapped t)
(format t "~&window mapped"))
nil)
(:unmap-notify (window)
(when (eql window win)
(setf window-mapped nil)
(format t "~&window unmapped"))
nil)
(:button-press ()
(return-from foo :quit))
(otherwise () t)))
(let* ((current-time (get-internal-real-time))
(delta-time (- current-time last-frame-time)))
(unless (zerop delta-time)
(move-objs objs
(coerce (/ delta-time internal-time-units-per-second) 'double-float)
win gc)
(setf last-frame-time current-time))
#+ignore (read-byte sock))))
#+ignore (close sock)))
(destroy-window win)
(close-display *display*))))


Sorry for long lines and wasted bandwidth,

Jonathan BAILLEUL

unread,
Dec 12, 2000, 6:12:18 AM12/12/00
to
Janis Dzerins a écrit :

>
> Hello.
>
> I'm writing a program that draws moving objects. I decided to
> represent their coordinates as floats so I have a struct:
>
> (defstuct moving-object
> x y dx dy)
>
> where x, y, dx, dy are all floats.
>
> Drawing function needs integers so I call TRUNCATE to get an integer
> but that conses like hell (50% of the space).
>
> Another operations that conses the same way (another 50%) is
> multiplication (I multiply dx and dy with delta-time).
>

Can I remember...

Under CMU/CL, the issue comes from the fact that floats are represented
as descriptors pointing to an object to allocate and free. Even if a
float can be coded as a 32bit word, CMU/CL can not handle it as a
"native" datatype because it needs a few bits for itself (GC stuff, for
instance), and that it handles only words of 32bits. Notice that other
numeric types are represented directly by its data and the few header
bits that all fit into a 32bit word.

The way to solve that issue is to provide specific compiler directives
to have CMU/CL resign his descriptor representation and use the float
instead. There are cases where it is simple and straightforward (declare
type, the), and others which are more complicated (cf documentation).

We went into that problem a few monthes ago for the glos project, but I
can't remember it perfectly...
Maybe Mr Toy and Himsek can answer with more accuracy?

This is really a critical problem, and I'd be glad if one could
establish a comprehensive CMU/CL tutorial with many case studies and
relevant solutions, considering I am not satisfied with CMU/CL
documentation on this point. I can start it (in january) from the case
studies extracted form our project (only if someone is interested) but I
will need assistance of course.

--
----------------------------
Bailleul Jonathan
DEA Informatique
LaBRI, Universite Bordeaux I

Raymond Toy

unread,
Dec 12, 2000, 9:39:39 AM12/12/00
to
>>>>> "Jonathan" == Jonathan BAILLEUL <bail...@labri.u-bordeaux.fr> writes:

Jonathan> This is really a critical problem, and I'd be glad if one could
Jonathan> establish a comprehensive CMU/CL tutorial with many case studies and
Jonathan> relevant solutions, considering I am not satisfied with CMU/CL
Jonathan> documentation on this point. I can start it (in january) from the case

Isn't the CMUCL User's Guide good enough? What more information do you
want?

Ray

Raymond Wiker

unread,
Dec 12, 2000, 1:21:25 PM12/12/00
to
ig...@ifi.uio.no (Igor V. Rafienko) writes:

> * Erik Naggum


>
> > | Drawing function needs integers so I call TRUNCATE to get an
> > | integer but that conses like hell (50% of the space).
> >
> > Is that because the integers turn into bignums, i.e., they need more
> > than the typical 29+1 bits that fixnums are made up of?
>
>

> Maybe I am missing something obvious, but why such a "strange" range
> for fixnums?. I'd expect something like 2^31-1 (yes, it's C legacy).
> Graham writes something like "a typical value of most-positive-fixnum
> could be 536870911" too. Why is that so? Are some bits (e.g. 2)
> reserved for some internal lisp purposes? Just curious...

Right. The missing bits are "tag bits", which are used to
encode the data type of every* data item.

*) For some value of every.

--
Raymond Wiker
Raymon...@fast.no

Janis Dzerins

unread,
Dec 12, 2000, 1:19:43 PM12/12/00
to
ig...@ifi.uio.no (Igor V. Rafienko) writes:

> Are some bits (e.g. 2) reserved for some internal lisp purposes?
> Just curious...

Yep, that's how it works. Read more about that in CDR-coding thread
(that has gone off topic now :), especially one post from Duane Rettig
about how it's done in Allegro CL on 32/64 bit architectures
(message-id: <4aea2i...@beta.franz.com> if that helps).

Joe Marshall

unread,
Dec 12, 2000, 2:17:37 PM12/12/00
to
Janis Dzerins <jo...@latnet.lv> writes:

> cl-user(4): (time (animation::clx-test))
> window mapped
> ; cpu time (non-gc) 11,570 msec user, 3,050 msec system
> ; cpu time (gc) 420 msec user, 0 msec system
> ; cpu time (total) 11,990 msec user, 3,050 msec system
> ; real time 15,746 msec
> ; space allocation:
> ; 226 cons cells, 117,638,416 other bytes, 0 static bytes
> :quit
> cl-user(5):
>
> Although I see that those almost 120Mb other bytes are collected
> immediately and have no real impact on performance, I'd like to know
> ways how to avoid allocating them anyway.

Why?

-----= Posted via Newsfeeds.Com, Uncensored Usenet News =-----
http://www.newsfeeds.com - The #1 Newsgroup Service in the World!
-----== Over 80,000 Newsgroups - 16 Different Servers! =-----

Janis Dzerins

unread,
Dec 12, 2000, 2:36:33 PM12/12/00
to
Joe Marshall <j...@content-integrity.com> writes:

> > Although I see that those almost 120Mb other bytes are collected
> > immediately and have no real impact on performance, I'd like to know
> > ways how to avoid allocating them anyway.
>
> Why?

Because I know it can be done (call it my ill fantasies :) and now I
want to know how. Allocation of that memory also takes time (exact
numbers provided upon request since now I have to go home).

Duane Rettig

unread,
Dec 12, 2000, 3:02:21 PM12/12/00
to
Janis Dzerins <jo...@latnet.lv> writes:

> Although I see that those almost 120Mb other bytes are collected
> immediately and have no real impact on performance, I'd like to know
> ways how to avoid allocating them anyway.

This is not entirely true; the more garbage is generated, the more
quickly the scavenges occur, and thus the faster generations are
incremented, thus affecting objects that stay live medium term. It
is not something to worry excessively over, but it can have a
non-trivial effect on performance.

> Here's the code. Good suggestions are welcome.

> (defstruct obj


> (x 0.0d0 :type double-float)
> (y 0.0d0 :type double-float)
> (dx 0.0d0 :type double-float)
> (dy 0.0d0 :type double-float))

Defstruct is in general good for elements of non-homogenous type; your
example here is in reality describing a vector of double-floats with
named accessors.

You can either define your own make-obj and accessors using make-array
with :element-type and aref, respectively, or you can use the :type
option to defstruct:

(defstruct (obj (:type (vector double-float (*))))


(x 0.0d0 :type double-float)
(y 0.0d0 :type double-float)
(dx 0.0d0 :type double-float)
(dy 0.0d0 :type double-float))

Note that the individual slot :type specifications are redundant.

Using this definition, the following example does not cons at all:

(defun foo (x)
(declare (optimize speed (safety 0))
(type (simple-array double-float (*)) x))
(setf (obj-dx x) (+ (obj-x x) (obj-y x)))
nil)

--
Duane Rettig Franz Inc. http://www.franz.com/ (www)
1995 University Ave Suite 275 Berkeley, CA 94704
Phone: (510) 548-3600; FAX: (510) 548-8253 du...@Franz.COM (internet)

0 new messages