Gerade oder ungerade Zahlen feststellen

Hier könnt Ihr gute, von Euch geschriebene Codes posten. Sie müssen auf jeden Fall funktionieren und sollten möglichst effizient, elegant und beispielhaft oder einfach nur cool sein.
Benutzeravatar
NicTheQuick
Ein Admin
Beiträge: 8808
Registriert: 29.08.2004 20:20
Computerausstattung: Ryzen 7 5800X, 64 GB DDR4-3200
Ubuntu 24.04.2 LTS
GeForce RTX 3080 Ti
Wohnort: Saarbrücken

Beitrag 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:
Benutzeravatar
Helle
Beiträge: 566
Registriert: 11.11.2004 16:13
Wohnort: Magdeburg

Beitrag 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
Kaeru Gaman
Beiträge: 17389
Registriert: 10.11.2004 03:22

Beitrag 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.
Der Narr denkt er sei ein weiser Mann.
Der Weise weiß, dass er ein Narr ist.
Benutzeravatar
Shardik
Beiträge: 746
Registriert: 25.01.2005 12:19

Beitrag 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...
Benutzeravatar
remi_meier
Beiträge: 1078
Registriert: 29.08.2004 20:11
Wohnort: Schweiz

Beitrag 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.
Benutzeravatar
Froggerprogger
Badmin
Beiträge: 855
Registriert: 08.09.2004 20:02

Beitrag 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)
Zuletzt geändert von Froggerprogger am 05.12.2006 18:48, insgesamt 2-mal geändert.
!UD2
Benutzeravatar
NicTheQuick
Ein Admin
Beiträge: 8808
Registriert: 29.08.2004 20:20
Computerausstattung: Ryzen 7 5800X, 64 GB DDR4-3200
Ubuntu 24.04.2 LTS
GeForce RTX 3080 Ti
Wohnort: Saarbrücken

Beitrag 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:
Benutzeravatar
Helle
Beiträge: 566
Registriert: 11.11.2004 16:13
Wohnort: Magdeburg

Beitrag 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 !
Benutzeravatar
Froggerprogger
Badmin
Beiträge: 855
Registriert: 08.09.2004 20:02

Beitrag 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.
!UD2
Benutzeravatar
Helle
Beiträge: 566
Registriert: 11.11.2004 16:13
Wohnort: Magdeburg

Beitrag 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
Antworten