Cython und große (>256 Bit) Integer

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
albertus
User
Beiträge: 52
Registriert: Mittwoch 7. Juli 2010, 14:48

Hallo,

kann Cython mit Integer umgehen die größer als 256 Bit sind? Gemeint sind damit die Datentypen die man mit "cdef" festlegt. Soweit ich weiß gehen die Originalen C Datentypen nur bis zu einer Größe von 64 Bit. Python (3.3.X) kann zwar sehr viel mehr, aber wird das auch direkt von Cython unterstützt?
Mit freundlichen Grüßen

Albertus
BlackJack

@albertus: Mit ``cdef`` kann man nur C-Datentypen definieren. Darum heisst das wohl auch ``cdef``. ;-)
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

Folgendes geht:

Code: Alles auswählen

cimport cython

cpdef klaus():
    cdef cython.long num = 10L
    return num
Ausgabe:

Code: Alles auswählen

>>> type(klaus())
<type 'long'>
>>> 
Keine Ahnung, wie man auf Cython-Seite damit rechnet, also ob die Operatoren in Cython angepasst sind oder man auf C-Funktionen ausweichen muss.
BlackJack

Ups, dann hat sich da einiges getan seit ich das letzte Mal Cython benutzt habe. :oops:
albertus
User
Beiträge: 52
Registriert: Mittwoch 7. Juli 2010, 14:48

jerch hat geschrieben:Folgendes geht:

Code: Alles auswählen

cimport cython

cpdef klaus():
    cdef cython.long num = 10L
    return num
Ausgabe:

Code: Alles auswählen

>>> type(klaus())
<type 'long'>
>>> 
Keine Ahnung, wie man auf Cython-Seite damit rechnet, also ob die Operatoren in Cython angepasst sind oder man auf C-Funktionen ausweichen muss.
Hallo Jerch,

versuche mal folgendes:

Code: Alles auswählen

cpdef klaus():
    cdef cython.long num = 340282366920938463463374607431768211456L
    return num
mit folgender setup.py:

Code: Alles auswählen

from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext

setup(
    cmdclass = {'build_ext': build_ext},
    ext_modules = [Extension("test_big_int", ["test_big_int.pyx",])]
)
beim ausführen der selbigen, bekomme ich dann folgendes von gcc mitgeteilt:
python3 setup.py build_ext --inplace
running build_ext
cythoning test_big_int.pyx to test_big_int.c
building 'test_big_int' extension
gcc -pthread -Wno-unused-result -DNDEBUG -fmessage-length=0 -O2 -Wall -D_FORTIFY_SOURCE=2 -fstack-protector -funwind-tables -fasynchronous-unwind-tables -g -fPIC -I/usr/include/python3.3m -c test_big_int.c -o build/temp.linux-x86_64-3.3/test_big_int.o
test_big_int.c: In function ‘__pyx_f_12test_big_int_klaus’:
test_big_int.c:595:17: warning: integer constant is too large for its type [enabled by default]
gcc -pthread -shared build/temp.linux-x86_64-3.3/test_big_int.o -L/usr/lib64 -lpython3.3m -o .../test_big_int.cpython-33m.so
Ein Test des Moduls in IDLE erzeugt folgende Ausgabe:
>>> test_big_int.klaus()
0
>>>
Getestet habe ich das jetzt unter Python 3.3.0 ich vermute es wird unter Python 2.7.2 ähnlich aussehen. Das Problem ist also c dazu zu bewegen, mit solchen und größeren Zahlen zu hantieren. Wie bekommt man das hin?
Mit freundlichen Grüßen

Albertus
BlackJack

@albertus: Im Grunde gar nicht. In C-Programmen würde man auf eine Bibliothek wie zum Beispiel die `libgmp` zurückgreifen. Dem C-Compiler kann man keine literalen ganzen Zahlen beibringen die grösser sind als man mit normalen ganzzahligen C-Datentypen darstellen kann. An der Stelle wird man einfach Python `int`\s verwenden müssen.

Edit: Was möchtest Du denn *eigentlich* machen?
albertus
User
Beiträge: 52
Registriert: Mittwoch 7. Juli 2010, 14:48

BlackJack hat geschrieben:@albertus: Im Grunde gar nicht. In C-Programmen würde man auf eine Bibliothek wie zum Beispiel die `libgmp` zurückgreifen. Dem C-Compiler kann man keine literalen ganzen Zahlen beibringen die grösser sind als man mit normalen ganzzahligen C-Datentypen darstellen kann. An der Stelle wird man einfach Python `int`\s verwenden müssen.
Tja so was habe ich mir auch schon gedacht, ich hatte allerdings die Hoffnung das Cython die Python `int\s als C `int\s verwenden würde, dem ist wohl nicht so, Schade.
BlackJack hat geschrieben: Edit: Was möchtest Du denn *eigentlich* machen?
Eigentlich nichts Weltbewegendes. Aus Interesse beschäftige ich mich gerade mit Kryptographie und da ging es unter anderem auch, um das Thema Primitivwurzeln und aus einer Laune heraus kam ich auf den Gedanken, solche aus etwas ;-) größeren Zahlen zu berechnen. Mit Python war das schnell umgesetzt und ist auch schnell. Da ich aber sowieso Cython mal ausprobieren wollte, dachte ich mir, warum nicht das Skript so abändern, dass es mit Hilfe von Cython in ein C-Programm compiliert werden kann. Nun ja, dann halt nicht.
Mit freundlichen Grüßen

Albertus
BlackJack

@albertus: Cython kann die Python `int`\s nur solange in C `int`\s wandeln, solange die nicht grösser werden als der grösste Ganzzahltyp den der C-Compiler halt so kann. Und C-Compiler versuchen in der Regel bei dem zu bleiben was die CPU in einem oder maximal zwei Registern unterbringen kann. Denn nur was der Prozessor direkt kann ist schnell und in wenigen Maschinenspracheanweisungen ausgedrückt. Wenn man mehr will, braucht man dafür Subroutinen und wenn man die eh schon braucht, kann man eine Bibliothek wie `libgmp` einbinden die mit beliebig grossen Zahlen rechnen kann. Beschränkung ist natürlich der Speicher.
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

@albertus:
Habe das Beispiel mal mit Deiner großen Zahl probiert - nun, es funktioniert bei mir (Python 2.7, Cython 0.15.1). Daraufhin hab ich mir mal den Cython-C-code angeschaut:

Code: Alles auswählen

static PyObject *__pyx_int_340282366920938463463374607431768211456L;
...
__pyx_int_340282366920938463463374607431768211456L = PyLong_FromString((char *)"340282366920938463463374607431768211456", 0, 0);
Damit wird klar, warum das geht - Cython transferiert die Literale zur Laufzeit mittels der PyLong_FromString-Funktion. Das macht es übringens immer so, also auch für kurze eigentlich C-gängige Längen. Interessant, dass es unter Python 3 nicht funktioniert, hier wäre der Cythoncode aufschlussreich (hab kein Python 3 hier). Die Namensgebung des Pointers finde ich kritisch, da werden diverse Compiler für sehr große Zahlen streiken (Microsoft und ICC erlauben nur 2048 Zeichen).

Man man, der Cythoncode böse mit GOTOs gespickt ;)

@zum eigentlichen Problem:
Sowas würde ich eher in C z.B. mit GMP machen, da jedes bisschen Boilerplate bei sowas nervt und Cython die ganze Pythonschicht mitschleppen muss, was aus Problemsicht Boilerplate ist. :lol:
BlackJack

@jerch: Cython benutzt GOTO in C wofür in Python ``except`` und ``finally`` benutzt werden. Das sind ja im Grunde auch eine Art Sprungmarken zu denen ”bedingungslos” gesprungen wird, wenn ein Fehler auftritt. Seit dem ich so eine Verwendung früher auch schon in anderen Programmen gesehen habe, sind meine C-Programme auch ”voll” von GOTOs, wenn es den Code an der Stelle vereinfacht, statt für jeden Fehlerrückgabecode von aufgerufenen Funktionen eine weitere Verschachtelungsebene aufzumachen.
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Basierend auf BlackJack: Für Fehlerbehandlungen am Ende von Funktionen ist es ja durchaus üblich, dass man sozusagen aus einer Stelle mitten im Code mit nem GOTO im Bedarfsfall zur passenden Fehlerbehandlung - oder in C wohl meistens eher: "Abräumaktion" (`free()` bzw `DECREF()` für CPython, `fclose()` usw.) - springt. Ein solches Vorgehen kann sehr schön Code-Duplizierungen vermeiden. Verpönt ist es eigentlich nur, wenn mehrfach gesprungen wird oder wenn von unten nach oben gesprungen wird, oder ähnliche Späße.

Gut, man kann sagen, dass man sowas grundsätzlich nicht verwenden möchte, weil einem das schon zu undurchsichtig ist. Dann müsste man aber konsequenterweise auch auf `break`- und `continue`-Statements, sowie ggf noch auf `return`s innerhalb von Schleifen, verzichten. Ob das Programmieren dadurch wirklich einfacher und lesbarer wird, liegt wohl im Auge des Betrachters (um es mal halbwegs wertfrei auszudrücken).
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

snafu hat geschrieben:Basierend auf BlackJack: Für Fehlerbehandlungen am Ende von Funktionen ist es ja durchaus üblich, dass man sozusagen aus einer Stelle mitten im Code mit nem GOTO im Bedarfsfall zur passenden Fehlerbehandlung - oder in C wohl meistens eher: "Abräumaktion" (`free()` bzw `DECREF()` für CPython, `fclose()` usw.) - springt. Ein solches Vorgehen kann sehr schön Code-Duplizierungen vermeiden. Verpönt ist es eigentlich nur, wenn mehrfach gesprungen wird oder wenn von unten nach oben gesprungen wird, oder ähnliche Späße.

Gut, man kann sagen, dass man sowas grundsätzlich nicht verwenden möchte, weil einem das schon zu undurchsichtig ist. Dann müsste man aber konsequenterweise auch auf `break`- und `continue`-Statements, sowie ggf noch auf `return`s innerhalb von Schleifen, verzichten. Ob das Programmieren dadurch wirklich einfacher und lesbarer wird, liegt wohl im Auge des Betrachters (um es mal halbwegs wertfrei auszudrücken).
Für solche Aufräumaktionen nutze ich frühe Returns mit entsprechenden Aufräumfunktionen (evtl. plus Errorstruct). Ein generelles Springen beim Ressourcenaufbau geht eh oft nicht, da das nötige Aufräumen von den Ressourcen, die man bereits alloziert hat, abhängt.
Ich hatte mal für ein Projekt mit longjmp rumhantiert, aber das ist eigentlich "GOTO gone wild", da hiermit Sprünge über Funktionen hinweg möglich werden. Hab ich ganz schnell wieder fallen gelassen, da ich meiner eigenen Codedisziplin nicht traute.
GOTOs habe ich selbst bisher nur in 2 oder 3 Fällen überhaupt eingesetzt, wobei ich damit Srünge bei komplizierten Bedingungen vereinfachen konnte (der Kommentar war dafür um so länger).
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Ist wohl eine Stil-Frage. Es gibt Code, bei dem Zeiger zunächst mit NULL initalisiert werden und unter dem Label am Ende der Funktion steht dann sowas wie:

Code: Alles auswählen

out:
    if (foo != NULL)
        free(foo);
    if (bar != NULL)
        free(bar);
    if (baz != NULL)
        free(baz);
    return result;
Denkbar ist hier natürlich auch ein kleines Makro a la `FREE`, das den vorherigen Test auf NULL übernimmt. Der Vorteil ist einfach, dass man für 3-4 Speicheranforderungen nicht in jedem Fehlerzweig explizit die bis dahin gemachten Alloziierungen mitbehandeln muss. Wenn etwas schiefgeht, dann macht man ein simples `goto out`. Und falls nichts schiefgeht, wird `out:` ja trotzdem abgearbeitet - also quasi Aufräumen nach Erfolg.

Aber wie gesagt: Ich will hier keinem was aufzwingen und bin auch kein regelmäßiger C-Programmierer. Neben dem persönlichen Geschmack können unter Umständen auch Performance-Frage eine Rolle spielen. Da ist natürlich jeder nicht zwingende Test einer zuviel.
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

@snafu:
In C sollte man alle Heap-Pointer, die nicht Rückgabe einer Funktion (z.B. "Konstruktor") sind, mit NULL initialisieren (good practise). Nicht zu vergessen der nachfolgende Check, bevor man dereferenziert. Für Stackpointer ist das Ganze irrelevant.
Dein gezeigtes Szenario ist eher selten - es geht davon aus, dass man innerhalb einer Funktion (nur dort funktioniert GOTO) mehrere Heap-Pointer alloziert und am Ende wieder aufräumt. Das riecht nach "monolithischen" Funktionen mit wenig Kapselung. Ich nutze lieber "objektorientiertes" C, schreibe früh Structs mit "Konstruktoren" und "Destruktoren", wobei die Destruktoren tatsächlich eine ähnliche Struktur zu Deinem Gezeigten haben (wobei man auf Nullpointer nicht testen würde).
BlackJack

@jerch: Ich schreibe C auch eher „objektorientiert” mit Strukturen und `create_*()`- und `destroy_*()`-Funktionen und finde das Szenario gar nicht so selten. Man erstellt halt in Funktionen ab und zu mal verschiedene Objekte, zum Beispiel ein oder zwei Datenstrukturen, einen Iterator über den Daten in die Struktur(en) eingelesen werden, und dann einen weiteren über den die verarbeiteten Daten in eine Ergebnisstruktur übertragen werden, die dann zurück gegeben wird. Und alles ausser der Ergebnisstruktur muss halt am Ende der Funktion — oder im Fehlerfall — wieder aufgeräumt werden.
Antworten