Voodoo and numbers
| Date | 1998-11-02 00:01 |
| From | Andre Bartetzki |
| Subject | Voodoo 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 |
| Date | 1998-11-02 16:43 |
| From | Ed Hall |
| Subject | Re: 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
|