Seite 3 von 4

Verfasst: 05.12.2006 11:59
von NicTheQuick
@Helle, Shardik:
Das ist zwar beides weitere Möglichkeiten, aber ich glaube nicht, dass sie so
schnell sind wie die anderen, weil ihr Procedures benutzt.
Aber falls ihr nur mit ASM angeben wolltet, ist das okay. :wink:

Verfasst: 05.12.2006 12:11
von Helle
In der Praxis würde es bei mir so aussehen:

Code: Alles auswählen

!test byte[v_pruefling],1
!jz l_even
;mache was wenn ungerade
!jmp l_weiter
even:
;mache was wenn gerade
weiter:
Das hat nichts mit Angabe zu tun, dass ist ganz einfach am schnellsten.
Auf Prozeduren oder Macros kann man da sehr gut verzichten!
Gruss
Helle

Verfasst: 05.12.2006 12:45
von Kaeru Gaman
ich würde da nicht unbedingt auf ein macro verzichten wollen,
und ich würde auch ein macro einer proc vorziehen.

aber das argument von Helle besticht:
ein tester sollte nicht unbedingt die getestete variable verändern.

das tut aber mein erster vorschlag als Macro formuliert auch nicht:

Code: Alles auswählen

Macro IsOdd(number)
  (number & 1)
EndMacro

A = -17
While A < 18
   A$ = Str(A) + " => " + Str(IsOdd(A)) 
   Debug A$
   A+1 
Wend
...und damit endspricht das exact dem, was NTQ auch gepostet hatte.

ob das wesentlich langsamer ist als Helles ASM-lösung, wage ich noch zu bezweifeln.
darüber mache ich mir gedanken, wenn ich eine million Odd/Even-test pro sekunde brauche.

den ASM-code jedesmal wieder einzufügen wäre mir persönlich einfach zuviel aufwand.

Verfasst: 05.12.2006 13:40
von Shardik
NicTheQuick hat geschrieben: @Helle, Shardik:
Das ist zwar beides weitere Möglichkeiten, aber ich glaube nicht, dass sie so schnell sind wie die anderen, weil ihr Procedures benutzt.
Aber falls ihr nur mit ASM angeben wolltet, ist das okay. :wink:
Das mit dem Angeben könnte man wohl wirklich so verstehen :lol:

Ich habe aber etwa mein Posting zur etwa gleichen Zeit wie Helle abgeschickt und kannte Helles Code vorher nicht...

[Wirkliches Angeben] 8)
Wenn man wie ich seit 1982 u.a. in Assembler programmiert (Prozessoren Z80, 6502, 6808, 680x0, 8088, 8086, 80x86, IBM Großrechner-Prozessor), seit einer Zeit also, in der die meisten Teilnehmer dieses Forums wohl noch gar nicht geboren waren, wundere ich mich, wieso überhaupt so viele ernsthafte Beiträge zu einem so trivialen Thema gepostet wurden...
[/Wirkliches Angeben]

Nur zum Verständnis eine Anekdote: mein erster Mikrocomputer - ein Tandy Radio Shack TRS-80 Model 1 (1981 gebraucht gekauft) - hatte 16 KB Speicher und einen Z-80 Prozessor mit 1,774 MHz und man mußte mit jedem Byte möglichst sparsam umgehen. Als ich zu diesem Rechner eine Speichererweiterung von 32 KB kaufte, fragte man mich im Laden, wozu ich die denn brauchen würde. Es sei doch nur schlechter Programmierstil, wenn man so viel Speicher braucht... :lol:

Im Prinzip habe ich mein Beispiel gebracht, um zu verdeutlichen, was bei NicTheQuicks Code in Assembler passiert. Sorry, wenn das nicht 'rüberkam... :roll:

In der Praxis hätte ich selbst wohl auch das IsOdd-Makro von NicTheQuick verwendet... Bei so einer kurzen Aktion spielt die Performance praktisch keine Rolle. Aber im Prinzip ist es seit Urprogrammierzeiten (siehe oben) korrekt, daß Makros immer schneller sind als Prozeduren, daß bei größeren Makros aber (durch die ständige Wiederverwendung des gleichen Codes) auch der Speicherbedarf stetig anwächst und der Geschwindigkeitsgewinn im Vergleich mit Prozeduren dann gegen Null strebt...

Verfasst: 05.12.2006 17:49
von remi_meier
Interessant:
http://de.wikipedia.org/wiki/Modulo_(Rest)

Code: Alles auswählen

a = -1
Debug -1 % 2
Debug a % 2
Jep, Feature, % 2 wird optimiert nach & 1, das Gleiche mit allen anderen
Zweierpotenzen.

Irgendwie kann ich mit einem negativen Operanden bei Modulo nix anfangen.
Also mich stört es nicht, dass es für negative Zahlen nicht immer geht.

Verfasst: 05.12.2006 18:40
von Froggerprogger
Das Verhalten von PB bei "a % n" mit n Zweierpotenz und a Variable kann gefährlich sein, wenn man z.B. C oder Java nach PB übersetzen will, da dort ein anderes Verhalten ist.
Also z.B.: liefert Java:

Code: Alles auswählen

int a = -7;
System.out.println(a % 4); // liefert -3
C:

Code: Alles auswählen

int a = -7;
printf("%i", (a % 4)); // liefert -3
PB:

Code: Alles auswählen

a = -7
Debug a % 4; liefert 1
Machen also irgendwelche Algorithmen davon z.B: per Abs(x % 128) Gebrauch, um eine beliebige Zahl auf einen Wert zwischen 0 und 127 zu hashen, so würde die direkte Übersetzung zu schwer zu findenen Bugs im Programmverhalten führen, da bei Java und C z.B. Abs(-5 % 128) = 5, während bei PB Abs(-5 % 128) = 123 ist.

Nochwas zur Mathematik dahinter:
(Danke für den Link, remi)
Es gibt also die 'mathematische Version', die einen Wert auf seinen Rest zwischen 0 und n-1 abbildet, und eine 'symmetrische Version', die einen Wert auf seinen Rest zwischen -(n-1) und n-1 abbildet und dabei die Symmetrie (-a) % b = - (a % b) erhält.
(Und dann gibt es noch die PB-Version, die für Zweierpotenzen doch wieder unsymmetrisch ist).

Sieht man aber alle als Funktion auf einen Repräsentanten der Restklasse, ist nichts davon 'falsch' (nichteinmal das andersartige Verhalten von PB bei modulo Zweierpotenz) sondern alles korrekt:

So gibt es z.B. bei modulo 3 die 3 Klassen:

Klasse [0] besteht aus ..., -6, -3, 0, 3, 6, ...
Klasse [1] besteht aus ..., -5, -2, 1, 4, 7, ...
Klasse [2] besteht aus ..., -4, -1, 2, 5, 8, ...

Zur Identifizierung der Klasse kann deren Repräsentant frei gewählt werden, also gilt z.B. [0] = [-3] = [18]... also ist auch z.B. -5 % 3 = - 2 nicht verkehrt, sondern es wird nur auf einen anderen Repräsentanten abgebildet. Selbst sowas Abstruses wie -5 % 3 = -176 wäre also nicht falsch. Insbesondere ist wie oben in den Beispielen -7 % 4 = -3 (Java+C) dann genauso richtig wie -7 % 4 = 1 (PB)

Möchte man bei "modulo n" definitiv einen positiven Repräsentanten zwischen 0 und n-1 haben (wie es oft auch von vorneherein in mathematischen Definitionen von mod verlangt wird), kann man folgendes machen:

Code: Alles auswählen

Procedure PosMod(a, n) ; return a % n with a in [0, n-1]
  Protected b.l
  b = a % n
  If b < 0
    ProcedureReturn b + n ; 'shift representative' by n
  Else
    ProcedureReturn b
  EndIf
EndProcedure

Debug PosMod(-5, 3)
Debug PosMod(-4, 3)
Debug PosMod(-3, 3)

Verfasst: 05.12.2006 18:47
von NicTheQuick
remi_meier hat geschrieben:Irgendwie kann ich mit einem negativen Operanden bei Modulo nix anfangen.
Also mich stört es nicht, dass es für negative Zahlen nicht immer geht.
Warum? Ist doch praktisch, z. B. bei Winkeln:

Code: Alles auswählen

Procedure Modulo(a.l, b.l)
  If b < 0 : b = -b : EndIf
  a % b
  If a < 0 : a + b : EndIf
  ProcedureReturn a
EndProcedure

For x.l = -360 To 360 Step 36
  Debug Str(x) + " % 360 = " + Str(Modulo(x, -360))
Next
Oder für Floats: (geht natürlich auch mit Doubles)

Code: Alles auswählen

Procedure.f ModuloF(a.f, b.f)
  If b < 0 : b = -b : EndIf
  a - Int(a / b) * b
  If a < 0 : a + b : EndIf
  ProcedureReturn a
EndProcedure

a.f = -6.2831
While a < 6.2831
  Debug StrF(a) + " % 6.2831 = " + StrF(ModuloF(a, 6.2831))
  a + 0.4
Wend
///Edit:
FroggerProgger war schneller und ein kleines, aber nur ganz bissl
ausführlicher. :wink:

Verfasst: 05.12.2006 18:58
von Helle
Aber interessant:

Code: Alles auswählen

a.q = -7 
Debug a % 4   ; liefert -3!
Gruss
Helle

P.S.: Könnte man fast als Bug bezeichnen :D !

Verfasst: 05.12.2006 19:09
von Froggerprogger
Repeat "It's a feature" ForEver

PB scheint eine leicht randomisierte Modulo-Funktion zu besitzen. Halten wir fest:
* uneinheitlich mit anderen Sprachen
* uneinheitlich mit sich selbst bei Verwendung anderer Datentypen

Obgleich alles im mathematischen Sinne richtig ist, wäre ein einheitliches Verhalten wäre schon wünschenswert.

Mathematisch richtig - wenn auch nicht sehr praktisch - wäre ja auch a % n = a für beliebig a und n, denn insbesondere a ist Repräsentant seiner Restklasse. Also wäre ein Festlegen auf eine konsequente 'mathematische' oder konsequente 'symmetrische' Version schon sehr schön.

Andernfalls müsste man den Wikipedia-Artikel erweitern.

Verfasst: 05.12.2006 19:26
von Helle
Habe mir mal die ASM-Ausgabe von PB dazu angeschaut: Bei z.B. Long "optimiert" PB zu

Code: Alles auswählen

; a1.l=a%4
  MOV    ebx,dword [v_a]
  AND    ebx,3
  MOV    dword [v_a1],ebx   
während für Quad die externe Funktion "Mod64" aufgerufen wird.
Bei "AND ebx,3" fällt natürlich das Vorzeichen weg.

Gruss
Helle