Author |
Message |
Dr. Spankenstein
Joined: Mar 03, 2007 Posts: 136 Location: Cambridge
Audio files: 1
|
Posted: Fri May 18, 2007 8:29 am Post subject:
Strange mathematical behaviour in ChucK |
|
|
Can anybody explain why chuck will tell me that:
0.83 == 0.83
yet
1.333333 != 1.333333
I'm finding it incredibly infuriating! It happens when I'm performing the calculation many times per second (sorry for being a little vague) |
|
Back to top
|
|
|
Kassen
Janitor
Joined: Jul 06, 2004 Posts: 7678 Location: The Hague, NL
G2 patch files: 3
|
Posted: Fri May 18, 2007 9:16 am Post subject:
|
|
|
Oh, dear oh, dear, oh dear! That sounds odd.
I ran a quick test;
Code: |
if (1.333333 == 1.333333 ) <<<"yeah">>>;
else <<<"oops">>>; |
And I get a "yeah" there. Could you give a example where ChucK messes this up? I gather this behaviour occurs in a more complicated setup? Likely it's a rounding error that you're suffering from, if it only uccors when calculating something "many times per second" it might also be a subtle timing issue?
Basically; could you write a program that's a simple as possible yet still has this error? _________________ Kassen |
|
Back to top
|
|
|
Dr. Spankenstein
Joined: Mar 03, 2007 Posts: 136 Location: Cambridge
Audio files: 1
|
Posted: Fri May 18, 2007 9:31 am Post subject:
|
|
|
I am willing to e-mail you my code but then again it would mean having to traipse through it.
If you dont mind then I dont mind!
Theres a around 600 lines of the stuff tho.....
I honestly cant think of a way around it as it is very specific to the code. The problem arises when calculations are done very quickly ...or in this case the tempo has increased.
Rhys
PS. I have just got around it by adding a seperate counter that does the action and avoids the aforementioned error. Its a bodge job but it works. |
|
Back to top
|
|
|
Kassen
Janitor
Joined: Jul 06, 2004 Posts: 7678 Location: The Hague, NL
G2 patch files: 3
|
Posted: Fri May 18, 2007 9:43 am Post subject:
|
|
|
Could you considder stripping this program down to the elements that generate this equation? Right now I suspect it's a rounding issue but rounding shouldn't be affected by tempo, that's quite mysterious. If that's realy the case Ge will have to look at it but chances are Ge too will sugest simplifying the code to the most simple form that still has the issue.
Another posibility is that when tempo increases the timing relation between multiple shred will change which might cause issues.
If you realy can't strip it; sure, mail it (adress is above my posts) but if it's 600lines I'm not making promisses about disecting it quickly :¬) _________________ Kassen |
|
Back to top
|
|
|
Dr. Spankenstein
Joined: Mar 03, 2007 Posts: 136 Location: Cambridge
Audio files: 1
|
Posted: Fri May 18, 2007 10:18 am Post subject:
|
|
|
I gonna carry on with the bodge job versiob for the time being. I will need quite some time to figure out how it could be scaled down. If I find a way I'll let you know.
Thanks again for your help
Rhys |
|
Back to top
|
|
|
spencer
Joined: Aug 16, 2006 Posts: 53 Location: northern california
|
Posted: Mon May 21, 2007 12:19 pm Post subject:
|
|
|
Hi Rhys,
You've actually come upon a fundamental limitation of computing in general, and binary real number data representation in particular. Floating point numbers are represented as the sum of positive and negative powers of 2, e.g. 2.125 = 2 + 1/8 = 2^1 + 2^-3 = binary 10.001. However, just like numbers such as 1/7 can't be represented exactly in finite-precision decimal, there are plenty of real numbers within the range of representable values that can't be represented exactly in finite-precision (aka digital) binary. So there's always a danger when comparing floating point numbers that the conversion from a decimal number in your code to the binary number in the CPU will have a rounding error that makes it different than the number to which you are comparing it. For a minimal example of this, try this code: Code: | if( 4.0/3.0 != ( 4.0 * 5.2 * 5.2 * 5.2 ) / ( 3.0 * 5.2 * 5.2 * 5.2 ) )
<<< "rounding error", "" >>>;
|
The two expressions are legitimately equal, but the computer thinks they are different. Converting each decimal number to binary and then doing different calculations on them introduces enough rounding error to make them different numbers in binary representation. The solution is to ignore the rounding errors and only determine if the two numbers are equal up to some decimal place: Code: | if( Math.fabs( ( 4.0/3.0 ) - ( 4.0 * 5.2 * 5.2 * 5.2 ) / ( 3.0 * 5.2 * 5.2 * 5.2 ) ) > 0.000001 )
<<< "not equal", "" >>>;
|
Here, ChucK (and basically any other program executing this expression on a similar architecture to mine) correctly identifies the two sides of the comparison as equal.
I might be a little shaky on the theory but the gist of all this should be right.
Long story short: dont compare floats directly. Instead, compare the absolute value of their difference to some relatively small value.
spencer Last edited by spencer on Mon May 21, 2007 5:05 pm; edited 1 time in total |
|
Back to top
|
|
|
kijjaz
Joined: Sep 20, 2004 Posts: 765 Location: bangkok, thailand
Audio files: 4
|
Posted: Mon May 21, 2007 1:54 pm Post subject:
|
|
|
Wow! thanks spencer!
that explains how it happen really clearly..
at least for a non-professional-programmer like me.
cool and thanks. |
|
Back to top
|
|
|
Kassen
Janitor
Joined: Jul 06, 2004 Posts: 7678 Location: The Hague, NL
G2 patch files: 3
|
Posted: Tue May 22, 2007 2:36 am Post subject:
|
|
|
Seconded; good stuf. _________________ Kassen |
|
Back to top
|
|
|
Dr. Spankenstein
Joined: Mar 03, 2007 Posts: 136 Location: Cambridge
Audio files: 1
|
Posted: Tue May 22, 2007 3:02 am Post subject:
|
|
|
Brilliant!
Thanks for your clear insight Spencer you have been a great help to me and no doubt a lot of other people who might encounter this frustrating positive error.
I shall get implementing your thoughts on the solution right away!
Rhys |
|
Back to top
|
|
|
Dr. Spankenstein
Joined: Mar 03, 2007 Posts: 136 Location: Cambridge
Audio files: 1
|
Posted: Tue May 22, 2007 5:10 am Post subject:
|
|
|
I think I may have been a little hasty with my reply as I dont seem to fully grasp what is going just yet.
I figured that if I wanted to avoid rounding error that I could multiply the reoccuring decimal by a power of 10 and then find the absolute value and compare that to the other value that had gone through the same processes however, this doesnt work?
Code: |
<<<Math.fabs((0.33333333)*1000000)>>>;
// Returns a number
<<<Math.fabs((1/3)*1000000)>>>;
// Returns 0.000000, why?!
|
Can anyone tell me where I'm going wrong?
Thanks again
Rhys |
|
Back to top
|
|
|
chuckles
Joined: Apr 02, 2007 Posts: 72 Location: San Diego, California
|
Posted: Tue May 22, 2007 8:10 am Post subject:
|
|
|
Quote: | <<<Math.fabs((1/3)*1000000)>>>;
// Returns 0.000000, why?! |
I think it's still because of the "int" vs. "float" thing...
1/3 is .33333. But since they are both ints, the result is an int which, rounded, is 0. So multiplying that gives 0.
but 1./3 (and probably 1/3.) gives .33333 since one operand is a float and chuck does the calculation as floatingpoint.
at least that's how it looks from here...
c. |
|
Back to top
|
|
|
kijjaz
Joined: Sep 20, 2004 Posts: 765 Location: bangkok, thailand
Audio files: 4
|
Posted: Tue May 22, 2007 8:36 am Post subject:
|
|
|
you can also cast an int to float
by using $ float just behind what you want to convert.
for example
1 => int x;
<<< x / 3 >>>; // this is like 1/3
<<< x $ float / 3 >>>; // this would be 1.0 / 3, thus having a float result
<<< x * 1.0 / 3 >>>; // multiplying an int by a float also creates a float result
<<< 1.0 * x / 3 >>>;
<<< (x * 5.0 / 3) $ int >>>; // but we can convert back to int also |
|
Back to top
|
|
|
Dr. Spankenstein
Joined: Mar 03, 2007 Posts: 136 Location: Cambridge
Audio files: 1
|
Posted: Tue May 22, 2007 10:48 am Post subject:
|
|
|
Ah dammit, I was hoping that werent the case. Using math.fabs doesnt work for my code as it still says they are not equal.
However, I found another nifty way that gets around this rounding error
Code: |
if (Math.round(CurrentTime $ float * 1000000) == Math.round(DurationOfOneBar $ float * 1000000))
|
Multiplying each float by a factor of 10 depending on how accurate you want it to be then rounding to the nearest whole number does the trick!
Hope this helps anyone who might encounter this problem and once again, thanks for all your help guys.
Rhys |
|
Back to top
|
|
|
Kassen
Janitor
Joined: Jul 06, 2004 Posts: 7678 Location: The Hague, NL
G2 patch files: 3
|
Posted: Tue May 22, 2007 2:04 pm Post subject:
|
|
|
Cool trick, I have to warn that there is a small theoretical risk though.
What could theoretically happen is that the two values to be compared somehow end up on oposite sides of the line that devides wether we round up or down.
If Spencer's methopd fails then this is likely because you are performing your operations in such a way that you get relatively large rounding errors so you might need to increase the difference that can be between your two numbers.
On the bright side; the very high presision floats that ChucK uses are quite nice to have for many applications. _________________ Kassen |
|
Back to top
|
|
|
|