Csound Csound-dev Csound-tekno Search About

Voodoo and numbers

Date1998-11-02 00:01
FromAndre Bartetzki
SubjectVoodoo and numbers
Hi,

for a Csound project with alternative tunings I need a function to get
pitch-class and octave numbers from a given midi note number. The .orc file
below contains the calculation for the pitch-class number.  There are numbers
from 0 to 40 at pfield p4 in the .sco file. 
i1 is the fractional part of p4 divided by octaves
i2 is in fact p4's pitch-class number (0,1,2,...,11)
i3 should be the same as i2 (in case of integer numbers in p4)
i4 should also be the same 

If p4 is above 12 i3 shows a very strange behavior (see the output lines below):
This is the output for p4=14 :
    instr 1:  i1 = 0.167  i2 = 2.000  i3 = 1.000  i4 = 2.000
The pitchclass of 14 is 2 and i2, i3 and i4 should have this value. There is
something wrong with i3 resp. the int()-function. It seems to be not only a
problem with precision. 
If you take for example p4=26 (the next note with the same pitch-class) you get:
    instr 1:  i1 = 0.167  i2 = 2.000  i3 = 2.000  i4 = 2.000
Now i3 seems to be OK. In both cases there is the same fractional part i1 and
the same pitch-class i2. But the int-function gives different results!
This problem appears by nearly one third of all numbers above 12 (except
multiples of 3, where the fractional part is a non-periodical decimal number)
as you can see below.
I've used several Csound versions on a Mac PPC9500 with MacOS 7.55 and 8.0
with exactly the same results.
What's wrong here?


Andre


The files:

;----------------- test.orc
instr 1

i1 = frac(p4/12)
i2 = i1 * 12
i3 = int(i2)      ; truncating
i4 = int(i2+0.5)  ; rounding

print i1,i2,i3,i4

endin	
;----------------- end test.orc

;----------------- test.sco
i1	0 1 0 
i1	. . 1
i1	. . 2
i1	. . 3
i1	. . 4
i1	. . 5
i1	. . 6
i1	. . 7
i1	. . 8
i1	. . 9
i1	. . 10
i1	. . 11
i1	. . 12
i1	. . 13
i1	. . 14
i1	. . 15
i1	. . 16
i1	. . 17
i1	. . 18
i1	. . 19
i1	. . 20
i1	. . 21
i1	. . 22
i1	. . 23
i1	. . 24
i1	. . 25
i1	. . 26
i1	. . 27
i1	. . 28
i1	. . 29
i1	. . 30
i1	. . 31
i1	. . 32
i1	. . 33
i1	. . 34
i1	. . 35
i1	. . 36
i1	. . 37
i1	. . 38
i1	. . 39
i1	. . 40
; and so on
;----------------- end test.sco

;----------------- output:
new alloc for instr 1:
instr 1:  i1 = 0.000  i2 = 0.000  i3 = 0.000  i4 = 0.000
new alloc for instr 1:
instr 1:  i1 = 0.083  i2 = 1.000  i3 = 1.000  i4 = 1.000
new alloc for instr 1:
instr 1:  i1 = 0.167  i2 = 2.000  i3 = 2.000  i4 = 2.000
new alloc for instr 1:
instr 1:  i1 = 0.250  i2 = 3.000  i3 = 3.000  i4 = 3.000
new alloc for instr 1:
instr 1:  i1 = 0.333  i2 = 4.000  i3 = 4.000  i4 = 4.000
new alloc for instr 1:
instr 1:  i1 = 0.417  i2 = 5.000  i3 = 5.000  i4 = 5.000
new alloc for instr 1:
instr 1:  i1 = 0.500  i2 = 6.000  i3 = 6.000  i4 = 6.000
new alloc for instr 1:
instr 1:  i1 = 0.583  i2 = 7.000  i3 = 7.000  i4 = 7.000
new alloc for instr 1:
instr 1:  i1 = 0.667  i2 = 8.000  i3 = 8.000  i4 = 8.000
new alloc for instr 1:
instr 1:  i1 = 0.750  i2 = 9.000  i3 = 9.000  i4 = 9.000
new alloc for instr 1:
instr 1:  i1 = 0.833  i2 = 10.000  i3 = 10.000  i4 = 10.000
new alloc for instr 1:
instr 1:  i1 = 0.917  i2 = 11.000  i3 = 11.000  i4 = 11.000
new alloc for instr 1:
instr 1:  i1 = 0.000  i2 = 0.000  i3 = 0.000  i4 = 0.000
new alloc for instr 1:
instr 1:  i1 = 0.083  i2 = 1.000  i3 = 1.000  i4 = 1.000
new alloc for instr 1:
instr 1:  i1 = 0.167  i2 = 2.000  i3 = 1.000  i4 = 2.000
new alloc for instr 1:
instr 1:  i1 = 0.250  i2 = 3.000  i3 = 3.000  i4 = 3.000
new alloc for instr 1:
instr 1:  i1 = 0.333  i2 = 4.000  i3 = 4.000  i4 = 4.000
new alloc for instr 1:
instr 1:  i1 = 0.417  i2 = 5.000  i3 = 4.000  i4 = 5.000
new alloc for instr 1:
instr 1:  i1 = 0.500  i2 = 6.000  i3 = 6.000  i4 = 6.000
new alloc for instr 1:
instr 1:  i1 = 0.583  i2 = 7.000  i3 = 7.000  i4 = 7.000
new alloc for instr 1:
instr 1:  i1 = 0.667  i2 = 8.000  i3 = 7.000  i4 = 8.000
new alloc for instr 1:
instr 1:  i1 = 0.750  i2 = 9.000  i3 = 9.000  i4 = 9.000
new alloc for instr 1:
instr 1:  i1 = 0.833  i2 = 10.000  i3 = 10.000  i4 = 10.000
new alloc for instr 1:
instr 1:  i1 = 0.917  i2 = 11.000  i3 = 11.000  i4 = 11.000
new alloc for instr 1:
instr 1:  i1 = 0.000  i2 = 0.000  i3 = 0.000  i4 = 0.000
new alloc for instr 1:
instr 1:  i1 = 0.083  i2 = 1.000  i3 = 0.000  i4 = 1.000
new alloc for instr 1:
instr 1:  i1 = 0.167  i2 = 2.000  i3 = 2.000  i4 = 2.000
new alloc for instr 1:
instr 1:  i1 = 0.250  i2 = 3.000  i3 = 3.000  i4 = 3.000
new alloc for instr 1:
instr 1:  i1 = 0.333  i2 = 4.000  i3 = 3.000  i4 = 4.000
new alloc for instr 1:
instr 1:  i1 = 0.417  i2 = 5.000  i3 = 5.000  i4 = 5.000
new alloc for instr 1:
instr 1:  i1 = 0.500  i2 = 6.000  i3 = 6.000  i4 = 6.000
new alloc for instr 1:
instr 1:  i1 = 0.583  i2 = 7.000  i3 = 6.000  i4 = 7.000
new alloc for instr 1:
instr 1:  i1 = 0.667  i2 = 8.000  i3 = 8.000  i4 = 8.000
new alloc for instr 1:
instr 1:  i1 = 0.750  i2 = 9.000  i3 = 9.000  i4 = 9.000
new alloc for instr 1:
instr 1:  i1 = 0.833  i2 = 10.000  i3 = 9.000  i4 = 10.000
new alloc for instr 1:
instr 1:  i1 = 0.917  i2 = 11.000  i3 = 11.000  i4 = 11.000
new alloc for instr 1:
instr 1:  i1 = 0.000  i2 = 0.000  i3 = 0.000  i4 = 0.000
new alloc for instr 1:
instr 1:  i1 = 0.083  i2 = 1.000  i3 = 0.000  i4 = 1.000
new alloc for instr 1:
instr 1:  i1 = 0.167  i2 = 2.000  i3 = 2.000  i4 = 2.000
new alloc for instr 1:
instr 1:  i1 = 0.250  i2 = 3.000  i3 = 3.000  i4 = 3.000
new alloc for instr 1:
instr 1:  i1 = 0.333  i2 = 4.000  i3 = 3.000  i4 = 4.000
; ...
;-------------------

--------------------------------------------------
Andre Bartetzki http://www.kgw.tu-berlin.de/~abart
Studio fuer elektroakustische Musik http://www.kgw.tu-berlin.de/~abart/Steam/steam.html
Hochschule fuer Musik Berlin http://www.hfm-berlin.de

Tel. +49-30-4726629
Tel. +49-30-203092488

Date1998-11-02 16:43
FromEd Hall
SubjectRe: Voodoo and numbers
The phenomenon you report is normal.  (I'm not saying it's intuitive, but
it's normal.)  What's happening is that the result of dividing by 12 is
usually (2/3rds of the time) not able to be exactly represented as a
binary fraction.  This inexact result is either rounded up or down (exactly
how is based on the binary representation and thus isn't necessarily what
you'd expect looking at the decimal result).  If it's rounded down,
multiplying the result by 12 will yield a number just slightly less than
the original.  Printed to three decimal points, it will look exactly
like the original number, but taking the int() will show that it falls
slightly short of the full integer value.  Of course, if the result of
the devision happened to be rounded up, int() would simply remove the
miniscule fraction involved.

I wrote a C program which performed the exact same calculations, but
which showed 8 digits beyond the decimal.  Here is the result:

i1 = 0.00000000, i2 = 0.00000000, i3 = 0.00000000, i4 = 0.00000000
i1 = 0.08333334, i2 = 1.00000000, i3 = 1.00000000, i4 = 1.00000000
i1 = 0.16666667, i2 = 2.00000000, i3 = 2.00000000, i4 = 2.00000000
i1 = 0.25000000, i2 = 3.00000000, i3 = 3.00000000, i4 = 3.00000000
i1 = 0.33333334, i2 = 4.00000000, i3 = 4.00000000, i4 = 4.00000000
i1 = 0.41666666, i2 = 5.00000000, i3 = 5.00000000, i4 = 5.00000000
i1 = 0.50000000, i2 = 6.00000000, i3 = 6.00000000, i4 = 6.00000000
i1 = 0.58333331, i2 = 7.00000000, i3 = 7.00000000, i4 = 7.00000000
i1 = 0.66666669, i2 = 8.00000000, i3 = 8.00000000, i4 = 8.00000000
i1 = 0.75000000, i2 = 9.00000000, i3 = 9.00000000, i4 = 9.00000000
i1 = 0.83333331, i2 =10.00000000, i3 =10.00000000, i4 =10.00000000
i1 = 0.91666669, i2 =11.00000000, i3 =11.00000000, i4 =11.00000000
i1 = 0.00000000, i2 = 0.00000000, i3 = 0.00000000, i4 = 0.00000000
i1 = 0.08333337, i2 = 1.00000048, i3 = 1.00000000, i4 = 1.00000000
i1 = 0.16666663, i2 = 1.99999952, i3 = 1.00000000, i4 = 2.00000000
i1 = 0.25000000, i2 = 3.00000000, i3 = 3.00000000, i4 = 3.00000000
i1 = 0.33333337, i2 = 4.00000048, i3 = 4.00000000, i4 = 4.00000000
i1 = 0.41666663, i2 = 4.99999952, i3 = 4.00000000, i4 = 5.00000000
i1 = 0.50000000, i2 = 6.00000000, i3 = 6.00000000, i4 = 6.00000000
i1 = 0.58333337, i2 = 7.00000048, i3 = 7.00000000, i4 = 7.00000000
i1 = 0.66666663, i2 = 7.99999952, i3 = 7.00000000, i4 = 8.00000000
i1 = 0.75000000, i2 = 9.00000000, i3 = 9.00000000, i4 = 9.00000000
i1 = 0.83333337, i2 =10.00000000, i3 =10.00000000, i4 =10.00000000
i1 = 0.91666663, i2 =11.00000000, i3 =11.00000000, i4 =11.00000000
i1 = 0.00000000, i2 = 0.00000000, i3 = 0.00000000, i4 = 0.00000000
i1 = 0.08333325, i2 = 0.99999905, i3 = 0.00000000, i4 = 1.00000000
i1 = 0.16666675, i2 = 2.00000095, i3 = 2.00000000, i4 = 2.00000000
i1 = 0.25000000, i2 = 3.00000000, i3 = 3.00000000, i4 = 3.00000000
i1 = 0.33333325, i2 = 3.99999905, i3 = 3.00000000, i4 = 4.00000000
i1 = 0.41666675, i2 = 5.00000095, i3 = 5.00000000, i4 = 5.00000000
i1 = 0.50000000, i2 = 6.00000000, i3 = 6.00000000, i4 = 6.00000000
i1 = 0.58333325, i2 = 6.99999905, i3 = 6.00000000, i4 = 7.00000000
i1 = 0.66666675, i2 = 8.00000095, i3 = 8.00000000, i4 = 8.00000000
i1 = 0.75000000, i2 = 9.00000000, i3 = 9.00000000, i4 = 9.00000000
i1 = 0.83333325, i2 = 9.99999905, i3 = 9.00000000, i4 =10.00000000
i1 = 0.91666675, i2 =11.00000095, i3 =11.00000000, i4 =11.00000000
i1 = 0.00000000, i2 = 0.00000000, i3 = 0.00000000, i4 = 0.00000000
i1 = 0.08333325, i2 = 0.99999905, i3 = 0.00000000, i4 = 1.00000000
i1 = 0.16666675, i2 = 2.00000095, i3 = 2.00000000, i4 = 2.00000000
i1 = 0.25000000, i2 = 3.00000000, i3 = 3.00000000, i4 = 3.00000000

As you can see, the effects of rounding start to appear almost immediately,
although not enough to affect the result of the multiplication until 13
is reached.  The latter is due to the rounding performed as part of that
multiplication.

Welcome to the world that people who write numeric software have to deal
with every day.

To fix your original problem, I'd not divide at all, but instead multiply
by a number slightly greater than 1/12:

    i1 = frac(p4 * .08333334)

I've tested it, and it seems to work as you intended.  (By the way, it
is almost always better to multiply by the reciprocal than to divide--
it is much faster on just about any CPU made.  This is especially true
when scaling at audio rates, for example.)

		-Ed