Permalänk

Java. If sats fungerar ej.

Jag har ett program som ska räkna ut en fysikalisk formel. Men det är inte problemet. Det är så att if satsen släpper in tal som är mindre än b och tal som är inte större. Den filtrerar alltså inte ut de.

public class te { public static void main (String[] args) { double g = 9.82; double m = 0.05; double e = 10; double tal = 0; double b = 0; for (int n = 0; n<90; n++) { int i = 10; double A = (double) n/180*Math.PI; System.out.println ("Detta är: " + n + " grader " + A); double Vx = (double)i/100*Math.cos(A); double t = 4/Vx; double Vy = (double)i/100*Math.sin(A)*t-(g*t*t)/2; double V = Vy*Vy+Vx+Vx; double h = (double)i/100*t-(g*t*t)/2; double E = m*g*h+(m*V*V)/2; if (E>b) { System.out.println ("E: " + E + " och b är: " + b); b = E; tal = n; } } System.out.println ("Den största är: " + tal + "\n B är: " + b); } }

Ursäkta att koden är lite rörig. Men förstår inte varför den inte filtrerar ut de tal som är mindre än b. Någon som vet varför?

Det var nu jättelänge sen jag programmerade java så kan vara något väldigt litet fel.

Permalänk
Medlem

Din if-sats verkar fungera utmärkt. Kunde inte se något fel. Är du säker på ett du kollat ordentligt?:P Specifikt på E i slutet av utskriften. T.ex. E: 1.451602678119858E19 och b är: 8.1121664823886264E18
1.45 * 10 ^19 vs 8.11 * 10 ^ 18

Visa signatur

Spelrigg: 800D| i7 3930K@4,7 GHz - Custom WC | 32 GB Kingston HyperX Beast | 7970 GHz X-Edition |1x30 Dell U3011, 2x27" | Sennheiser HD650 | Xonar Essence STX |
Laptop: G74SX 17,3" 120 Hz 3D |
Server: Phenom II X4 955BE | Corsair XMS3 8 GB | 16 HDDs, 27 TB |
HTPCs: ASUS EEE Box 1.8 Ghz | Blu-Ray | OCZ Vertex 2 60 GB | 4 GB RAM |

Permalänk
Skrivet av Gnejs:

Din if-sats verkar fungera utmärkt. Kunde inte se något fel. Är du säker på ett du kollat ordentligt?:P Specifikt på E i slutet av utskriften. T.ex. E: 1.451602678119858E19 och b är: 8.1121664823886264E18
1.45 * 10 ^19 vs 8.11 * 10 ^ 18

Oj nu såg jag det. Fan då är det något annat fel med min uträkning. Men tack iaf:D

Permalänk
Medlem

Jag rekommenderar när man arbetar med precision och noggrannhet att inte använda sig av float och double om det är möjligt.
Speciellt inte i if-satser och dylikt. Det finns andra alternativ som BidDecimal, eller andra metoder för att jämnföra.

Kolla in detta tex.

double a = 0.1; double b = 0.1; double c = 0.3; if( a+b+a == c ) System.out.println("yeahaaa");

Sen försök inte deklarera variabler i en loop, lägg detta utanför.

Visa signatur

Corsair 16GB (4x4096MB) CL9 1600Mhz | Asus P8Z77-V PRO |
Samsung SSD Basic 830-Series 256GB | Intel Core i7 3770K 3,5Ghz |
Asus Xonar Essence STX | Noctua NH-U9B SE2 | Antec Performance One P280 | Corsair HX 850W 80+ Gold Modulär | MSI GTX 770

Permalänk
Medlem

Sen en annan sak du bör tänka på är.
Kolla igenom dina beräkningar, du deklarerar i till 10 i varje loop som du använder i dina beräkningar tex. varför?
Varför heter den i? Varför använder du i ?

Sen som jag sa innan deklarera inte variabler i loppen, det blir bara sämre prestanda och rörigare/grötigare.
Sen aldrig fel med lite mer mellanslag i beräkningar så man lätt se paranteser och dylikt.
Blir så mycket lättare att felsöka också.

Visa signatur

Corsair 16GB (4x4096MB) CL9 1600Mhz | Asus P8Z77-V PRO |
Samsung SSD Basic 830-Series 256GB | Intel Core i7 3770K 3,5Ghz |
Asus Xonar Essence STX | Noctua NH-U9B SE2 | Antec Performance One P280 | Corsair HX 850W 80+ Gold Modulär | MSI GTX 770

Permalänk
Medlem
Skrivet av NoPaiN^:

Sen som jag sa innan deklarera inte variabler i loppen, det blir bara sämre prestanda och rörigare/grötigare.

I Java bör man deklarera variabler i det scope de används. Det ger ingen prestandaförlust och du slipper "läckiga" variabelnamn som går att referera till genom hela metoden. Det går också att argumentera för att det blir tydligare att definiera variabler precis innan de används, då man på så vis slipper leta efter användningspositionen.

Visa signatur

Kom-pa-TI-bilitet

Permalänk
Medlem
Skrivet av Teknocide:

I Java bör man deklarera variabler i det scope de används. Det ger ingen prestandaförlust och du slipper "läckiga" variabelnamn som går att referera till genom hela metoden. Det går också att argumentera för att det blir tydligare att definiera variabler precis innan de används, då man på så vis slipper leta efter användningspositionen.

Jo men tanken var i denna kod TS hade skrivit, där han deklarerar lite variabler innan loopen, sen ytterligare variabler inne i loopen.
Bättre att försöka ha alla på samma ställe, sen att man inte får prestandaskillnader, jadu testa får du se.
Tycker det inte finns nån anledning att deklarera nya i loopen sen använda extra variabler utöver detta.
Tex, tal, n, e, b, i. Kan ju i prinicip skippa hälften av dom.

Visa signatur

Corsair 16GB (4x4096MB) CL9 1600Mhz | Asus P8Z77-V PRO |
Samsung SSD Basic 830-Series 256GB | Intel Core i7 3770K 3,5Ghz |
Asus Xonar Essence STX | Noctua NH-U9B SE2 | Antec Performance One P280 | Corsair HX 850W 80+ Gold Modulär | MSI GTX 770

Permalänk
Medlem
Skrivet av NoPaiN^:

Jo men tanken var i denna kod TS hade skrivit, där han deklarerar lite variabler innan loopen, sen ytterligare variabler inne i loopen.
Bättre att försöka ha alla på samma ställe, sen att man inte får prestandaskillnader, jadu testa får du se.
Tycker det inte finns nån anledning att deklarera nya i loopen sen använda extra variabler utöver detta.
Tex, tal, n, e, b, i. Kan ju i prinicip skippa hälften av dom.

Java är ett "högnivåspråk": Att du deklarerar en variabel inuti en loop innebär inte att Java gör en allokation och en deallokation för varje varv. Scope är mycket viktigare; om variabeln inte ska finnas utanför loopen ska den inte heller deklareras där. Ett exempel är iterator-variabler (for(int i=0; i<5; i++)) där i direkt efter loopen är fritt att använda i annan kod.

Jag instämmer i att det är många variabler igång. För att förtydliga att vissa är konstanta värden som aldrig kommer ändras kan man annotera dem med 'final'.

Visa signatur

Kom-pa-TI-bilitet

Permalänk
Datavetare
Skrivet av Teknocide:

Java är ett "högnivåspråk": Att du deklarerar en variabel inuti en loop innebär inte att Java gör en allokation och en deallokation för varje varv. Scope är mycket viktigare; om variabeln inte ska finnas utanför loopen ska den inte heller deklareras där. Ett exempel är iterator-variabler (for(int i=0; i<5; i++)) där i direkt efter loopen är fritt att använda i annan kod.

Jag instämmer i att det är många variabler igång. För att förtydliga att vissa är konstanta värden som aldrig kommer ändras kan man annotera dem med 'final'.

Faktum är att jag förutsatte att det var exakt som du skriver, men ett enkelt exempel visade att så inte är fallet.

public class Test { void foo(int x) { for (int i = 0; i < x; x++) { int y = x * i; System.out.println(y); } } void bar(int x) { for (int i = 0; i < x; x++) { System.out.println(x * i); } } }

Bytekod från javac

void foo(int); Code: 0: iconst_0 1: istore_2 2: iload_2 3: iload_1 4: if_icmpge 24 7: iload_1 8: iload_2 9: imul 10: istore_3 11: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 14: iload_3 15: invokevirtual #3 // Method java/io/PrintStream.println:(I)V 18: iinc 1, 1 21: goto 2 24: return void bar(int); Code: 0: iconst_0 1: istore_2 2: iload_2 3: iload_1 4: if_icmpge 22 7: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 10: iload_1 11: iload_2 12: imul 13: invokevirtual #3 // Method java/io/PrintStream.println:(I)V 16: iinc 1, 1 19: goto 2 22: return

Dold text

Man tycker att foo och bar skulle generera exakt samma bytekod då variabeln y egentligen inte behövs. Men kompilerar man detta (i mitt fall med JDK 1.7.0_07, så inser man att kompilatorn verkligen gör EXAKT vad som står vilket leder till att foo är 2 instruktioner längre, en istore_3 för att skriva ner värdet som y får och en för att läsa upp det inför anropet till System.out.println.

Nu kanske JIT kompilator lyckas optimera bort detta, men initialt så kommer i alla fall foo ta lite längre tid att köra.

Motsvarande C99 (då C89 inte tillåter att man deklarerar variabler inuti en for sats) är

void foo(int x) { for (int i = 0; i < x; x++) { int y = x * i; printf(("%d", y); } } void bar(int x) { for (int i = 0; i < x; x++) { printf("%d\n", x * i); } }

och med optimeringar på så blir det exakt samma instruktioner för båda funktionerna.

0000000000000020 <foo>: 20: 48 83 ec 08 sub rsp,0x8 24: 85 ff test edi,edi 26: 7e 1d jle 45 <foo+0x25> 28: 0f 1f 84 00 00 00 00 nop DWORD PTR [rax+rax*1+0x0] 2f: 00 30: 31 d2 xor edx,edx 32: be 00 00 00 00 mov esi,0x0 37: bf 01 00 00 00 mov edi,0x1 3c: 31 c0 xor eax,eax 3e: e8 00 00 00 00 call 43 <foo+0x23> 43: eb eb jmp 30 <foo+0x10> 45: 48 83 c4 08 add rsp,0x8 49: c3 ret 0000000000000050 <bar>: 50: 48 83 ec 08 sub rsp,0x8 54: 85 ff test edi,edi 56: 7e 1d jle 75 <bar+0x25> 58: 0f 1f 84 00 00 00 00 nop DWORD PTR [rax+rax*1+0x0] 5f: 00 60: 31 d2 xor edx,edx 62: be 00 00 00 00 mov esi,0x0 67: bf 01 00 00 00 mov edi,0x1 6c: 31 c0 xor eax,eax 6e: e8 00 00 00 00 call 73 <bar+0x23> 73: eb eb jmp 60 <bar+0x10> 75: 48 83 c4 08 add rsp,0x8 79: c3 ret

Dold text

Lite sökning på nätet visar att bl.a. IBM har en artikelserie just kring vad som genereras på byte-kod nivå givet en vis Java-kod-konstruktion och det verkar finnas massor med gotchas i Java likt detta...

Visa signatur

Care About Your Craft: Why spend your life developing software unless you care about doing it well? - The Pragmatic Programmer

Permalänk
Medlem
Skrivet av Yoshman:

Faktum är att jag förutsatte att det var exakt som du skriver, men ett enkelt exempel visade att så inte är fallet.

public class Test { void foo(int x) { for (int i = 0; i < x; x++) { int y = x * i; System.out.println(y); } } void bar(int x) { for (int i = 0; i < x; x++) { System.out.println(x * i); } } }

Dold text

Bytekod från javac

void foo(int); Code: 0: iconst_0 1: istore_2 2: iload_2 3: iload_1 4: if_icmpge 24 7: iload_1 8: iload_2 9: imul 10: istore_3 11: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 14: iload_3 15: invokevirtual #3 // Method java/io/PrintStream.println:(I)V 18: iinc 1, 1 21: goto 2 24: return void bar(int); Code: 0: iconst_0 1: istore_2 2: iload_2 3: iload_1 4: if_icmpge 22 7: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 10: iload_1 11: iload_2 12: imul 13: invokevirtual #3 // Method java/io/PrintStream.println:(I)V 16: iinc 1, 1 19: goto 2 22: return

Dold text

Man tycker att foo och bar skulle generera exakt samma bytekod då variabeln y egentligen inte behövs. Men kompilerar man detta (i mitt fall med JDK 1.7.0_07, så inser man att kompilatorn verkligen gör EXAKT vad som står vilket leder till att foo är 2 instruktioner längre, en istore_3 för att skriva ner värdet som y får och en för att läsa upp det inför anropet till System.out.println.

Nu kanske JIT kompilator lyckas optimera bort detta, men initialt så kommer i alla fall foo ta lite längre tid att köra.

Jag är rätt säker på att JITtern tar det där ganska omgående, men i vilket fall så har jag inte påstått att javac optimerar bort variabler inuti loopar. Vad jag menar är att det inte ger någon prestandafördel att deklarera variabler utanför loopen istället för inuti. Faktum är att det borde vara krångligare för JITtern att optimera bort en variabel som är definierad utanför det loop-scope den används i (om den även har fått ett initialt värde dvs.)

edit: Jag testade att kompilera nedanstående metoder

void foo(int x) { for (int i = 0; i < x; x++) { int y = x * i; System.out.printf("%d", y); } } void bar(int x) { int y; for (int i = 0; i < x; x++) { y = x * i; System.out.printf("%d", y); } }

Dold text

Resultat:

void foo(int); flags: Code: stack=6, locals=4, args_size=2 0: iconst_0 1: istore_2 2: iload_2 3: iload_1 4: if_icmpge 37 7: iload_1 8: iload_2 9: imul 10: istore_3 11: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 14: ldc #3 // String %d 16: iconst_1 17: anewarray #4 // class java/lang/Object 20: dup 21: iconst_0 22: iload_3 23: invokestatic #5 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 26: aastore 27: invokevirtual #6 // Method java/io/PrintStream.printf:(Ljava/lang/String;[Ljava/lang/Object;)Ljava/io/PrintStream; 30: pop 31: iinc 1, 1 34: goto 2 37: return LineNumberTable: line 7: 0 line 8: 7 line 9: 11 line 7: 31 line 11: 37 StackMapTable: number_of_entries = 2 frame_type = 252 /* append */ offset_delta = 2 locals = [ int ] frame_type = 250 /* chop */ offset_delta = 34 void bar(int); flags: Code: stack=6, locals=4, args_size=2 0: iconst_0 1: istore_3 2: iload_3 3: iload_1 4: if_icmpge 37 7: iload_1 8: iload_3 9: imul 10: istore_2 11: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 14: ldc #3 // String %d 16: iconst_1 17: anewarray #4 // class java/lang/Object 20: dup 21: iconst_0 22: iload_2 23: invokestatic #5 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 26: aastore 27: invokevirtual #6 // Method java/io/PrintStream.printf:(Ljava/lang/String;[Ljava/lang/Object;)Ljava/io/PrintStream; 30: pop 31: iinc 1, 1 34: goto 2 37: return LineNumberTable: line 15: 0 line 16: 7 line 17: 11 line 15: 31 line 19: 37 StackMapTable: number_of_entries = 2 frame_type = 253 /* append */ offset_delta = 2 locals = [ top, int ] frame_type = 249 /* chop */ offset_delta = 34

Dold text

Som synes blir bytecoden identisk.

Visa signatur

Kom-pa-TI-bilitet

Permalänk
Datavetare
Skrivet av Teknocide:

Jag är rätt säker på att JITtern tar det där ganska omgående, men i vilket fall så har jag inte påstått att javac optimerar bort variabler inuti loopar. Vad jag menar är att det inte ger någon prestandafördel att deklarera variabler utanför loopen istället för inuti. Faktum är att det borde vara krångligare för JITtern att optimera bort en variabel som är definierad utanför det loop-scope den används i (om den även har fått ett initialt värde dvs.)

Givet samma bytekod så lär jobbet för JIT:en vara identiskt då bytekoden aldrig skrivs om. Den man undrar är varför man inte försöker sig på optimeringar av bytekoden i javac. Enligt en av de IBM artiklar jag hittade när jag googlade på detta så utförde de tidiga versionerna av javac optimeringar på bytekoden likt vad en C/C++ kompilator gör med den maskinkod som genereras, men denna optimering togs bort rätt tidigt i Javas historia.

Även om JIT är bra så känns det ändå rent intuitivt som att lite optimeringar på bytekoden inte kan skada. När bytekoden skapas har ju kompilatorn tillgång till Javakoden och borde kunna göra mer intelligenta optimeringar än vad den JIT kan. Om inte så borde det rent teoretiskt vara bättre att kompilera ett C/C++ program till säg MIPS eller ARM maskinkod och sedan köra JIT översättning till x86 kod. Den idén faller naturligtvis på sin egen orimlighet, men det finns flera exempel på där man gjort binäröversättning (och inte emulering) från en arkitektur till en annan med väldigt gott resultat (Android för x86 är ett sådant exempel där man översätter applikationer som använt NDK från ARM maskinkod till x86 maskinkod). Ett annat sådant exempel är ju just JIT av bytekod i form av Java-bytekod eller CLR (.Net) där "binäröversättning" via JIT faktiskt ger rätt snabba program i många fall.

Som bytekoden är genererad i ditt fall så är ju slutresultatet det samma som om alla variabler deklarerats som första sak i metoden. Naturligtvis ett enkelt sätt att lösa det, men resultatet blir ju att objekt som är allokerade på heap:en blir levande (sett från GC:n) längre än vad som är strikt nödvändigt. Anledningen är antagligen att specifikationen för hur en JVM ska fungera säger att det ska finnas en statisk allokerad "array of local variables" (den som [a|d|i][load|store]_N instruktionerna jobbar mot) för varje metod (inte för varje lexikografisk "scope").

Visa signatur

Care About Your Craft: Why spend your life developing software unless you care about doing it well? - The Pragmatic Programmer