Note that there are some explanatory texts on larger screens.

plurals
  1. POProGuard can cause incorrect calculations
    primarykey
    data
    text
    <p>I have met a pretty strange bug. The following small piece of code uses a rather simple math.</p> <pre><code>protected double C_n_k(int n, int k) { if(k&lt;0 || k&gt;n) return 0; double s=1; for(int i=1;i&lt;=k;i++) s=s*(n+1-i)/i; return s; } </code></pre> <p><strong>Edit</strong> Using ProGuard can make it go wrong on some devices. I have it confirmed on HTC One S Android 4.1.1 build 3.16.401.8, but judging by e-mails I got, a lot of phones with Android 4+ are affected. For some of them (Galaxy S3), american operator-branded phones are affected, while international versions are not. Many phones are not affected. </p> <p>Below is the code of activity which calculates C(n,k) for 1&lt;=n&lt;25 and 0&lt;=k&lt;=n. On device mentioned above the first session gives correct results, but the subsequent launches show incorrect results, each time in different positions.</p> <p>I have 3 questions:</p> <ol> <li><p>How can it be? Even if ProGuard made something wrong, calculations should be consistent between devices and sessions.</p></li> <li><p>How can we avoid it? I know substituting <code>double</code> by <code>long</code> is fine in this case, but it is not a universal method. Dropping using <code>double</code> or releasing not-obfuscated versions is out of question.</p></li> <li><p>What Android versions are affected? I was quite quick with fixing it in the game, so I just know that many players have seen it, and at least the most had Android 4.0</p></li> </ol> <p>Overflow is out of question, because sometimes I see mistake in calculating <code>C(3,3)=3/1*2/2*1/3</code>. Usually incorrect numbers start somewhere in C(10,...), and look like a phone has "forgotten" to make some divisions.</p> <p>My SDK tools are 22.3 (the latest), and I have seen it in builds created by both Eclipse and IntelliJ IDEA.</p> <p>Activity code:</p> <pre><code>package com.karmangames.mathtest; import android.app.Activity; import android.os.Bundle; import android.text.method.ScrollingMovementMethod; import android.widget.TextView; public class MathTestActivity extends Activity { /** * Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); String s=""; for(int n=0;n&lt;=25;n++) for(int k=0;k&lt;=n;k++) { double v=C_n_k_double(n,k); s+="C("+n+","+k+")="+v+(v==C_n_k_long(n,k) ? "" : " Correct is "+C_n_k_long(n,k))+"\n"; if(k==n) s+="\n"; } System.out.println(s); ((TextView)findViewById(R.id.text)).setText(s); ((TextView)findViewById(R.id.text)).setMovementMethod(new ScrollingMovementMethod()); } protected double C_n_k_double(int n, int k) { if(k&lt;0 || k&gt;n) return 0; //C_n^k double s=1; for(int i=1;i&lt;=k;i++) s=s*(n+1-i)/i; return s; } protected double C_n_k_long(int n, int k) { if(k&lt;0 || k&gt;n) return 0; //C_n^k long s=1; for(int i=1;i&lt;=k;i++) s=s*(n+1-i)/i; return (double)s; } } </code></pre> <p>main.xml:</p> <pre><code>&lt;?xml version="1.0" encoding="utf-8"?&gt; &lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" &gt; &lt;TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:id="@+id/text" android:text="Hello World!" /&gt; &lt;/LinearLayout&gt; </code></pre> <p>Example of wrong calculation results (remember, it's different every time I try it)</p> <pre><code>C(0,0)=1.0 C(1,0)=1.0 C(1,1)=1.0 C(2,0)=1.0 C(2,1)=2.0 C(2,2)=1.0 C(3,0)=1.0 C(3,1)=3.0 C(3,2)=3.0 C(3,3)=1.0 C(4,0)=1.0 C(4,1)=4.0 C(4,2)=6.0 C(4,3)=4.0 C(4,4)=1.0 C(5,0)=1.0 C(5,1)=5.0 C(5,2)=10.0 C(5,3)=10.0 C(5,4)=30.0 Correct is 5.0 C(5,5)=1.0 C(6,0)=1.0 C(6,1)=6.0 C(6,2)=15.0 C(6,3)=40.0 Correct is 20.0 C(6,4)=90.0 Correct is 15.0 C(6,5)=144.0 Correct is 6.0 C(6,6)=120.0 Correct is 1.0 C(7,0)=1.0 C(7,1)=7.0 C(7,2)=21.0 C(7,3)=35.0 C(7,4)=105.0 Correct is 35.0 C(7,5)=504.0 Correct is 21.0 C(7,6)=840.0 Correct is 7.0 C(7,7)=720.0 Correct is 1.0 C(8,0)=1.0 C(8,1)=8.0 C(8,2)=28.0 C(8,3)=112.0 Correct is 56.0 C(8,4)=70.0 C(8,5)=1344.0 Correct is 56.0 C(8,6)=3360.0 Correct is 28.0 C(8,7)=5760.0 Correct is 8.0 C(8,8)=5040.0 Correct is 1.0 C(9,0)=1.0 C(9,1)=9.0 C(9,2)=36.0 C(9,3)=168.0 Correct is 84.0 C(9,4)=756.0 Correct is 126.0 C(9,5)=3024.0 Correct is 126.0 C(9,6)=10080.0 Correct is 84.0 C(9,7)=25920.0 Correct is 36.0 C(9,8)=45360.0 Correct is 9.0 C(9,9)=40320.0 Correct is 1.0 C(10,0)=1.0 C(10,1)=10.0 C(10,2)=45.0 C(10,3)=120.0 C(10,4)=210.0 C(10,5)=252.0 C(10,6)=25200.0 Correct is 210.0 C(10,7)=120.0 C(10,8)=315.0 Correct is 45.0 C(10,9)=16800.0 Correct is 10.0 C(10,10)=1.0 </code></pre>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload