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 |