Ctypes: String-Formatting mit c_float() verhält sich komisch

Python in C/C++ embedden, C-Module, ctypes, Cython, SWIG, SIP etc sind hier richtig.
Antworten
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Code: Alles auswählen

>>> libc = ctypes.CDLL('libc.so.6')
>>> libc.printf('Pi is %f\n', ctypes.c_double(3.14))
Pi is 3.140000
15
>>> libc.printf('Pi is %f\n', ctypes.c_float(3.14))
Pi is -0.013221
16
Analog dazu, weil ich schon an mir selbst gezweifelt habe:

Code: Alles auswählen

urx@murx:~$ cat test.c
#include <stdio.h>

int main() {
    printf("Pi is %f\n", 3.14);
    return 0;
}
urx@murx:~$ ./test
Pi is 3.140000
Ist ein c_float() aus Python bzw ctypes heraus etwas anderes und muss ich noch etwas zusätzlich beachten oder wie lässt sich das erklären?
Benutzeravatar
hendrikS
User
Beiträge: 420
Registriert: Mittwoch 24. Dezember 2008, 22:44
Wohnort: Leipzig

Bißchen grützig. Glaube nicht, daß das ein Feature ist. Na ja. Gut zu wissen.
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Macht wohl alles c_double() in ctypes:

Code: Alles auswählen

>>> libc.printf('PI is %f', ctypes.c_double(3.14159))
PI is 3.14159014
Ich spreche hier jetzt nur für ein 32-bit Linux-System (Ubuntu). Erfahrungsberichte auf anderen Plattformen wären nett. :)
BlackJack

@snafu: Das hat nichts mit `ctypes` zu tun, sondern mit `printf()` -- das ``%f`` steht nicht für den Typ `float` sondern erwartet als Argument ein `double`. Wenn es das nicht bekommt, wird halt Mist ausgegeben.
Benutzeravatar
hendrikS
User
Beiträge: 420
Registriert: Mittwoch 24. Dezember 2008, 22:44
Wohnort: Leipzig

BlackJack hat geschrieben:@snafu: Das hat nichts mit `ctypes` zu tun, sondern mit `printf()` -- das ``%f`` steht nicht für den Typ `float` sondern erwartet als Argument ein `double`. Wenn es das nicht bekommt, wird halt Mist ausgegeben.
Das würde ich mal so nicht unterschreiben. Auch wenn ich ein float übergebe sollte bei %f was richtiges rauskommen.
BlackJack

@hendrikS: Wenn Du ein `c_float` bei `ctypes()` übergibst vielleicht, aber an das C-`printf()` darf man definitiv kein `float` auf den Stack packen. (Ausser natürlich wenn `float` und `double` indentisch sind.)
Benutzeravatar
hendrikS
User
Beiträge: 420
Registriert: Mittwoch 24. Dezember 2008, 22:44
Wohnort: Leipzig

Bei gnu.org habe ich das folgende gefunden:
Without a type modifier, the floating-point conversions use an argument of type double. (By the default argument promotions, any float arguments are automatically converted to double.)
Bleibt also jetzt die Frage warum es mit ctypes nicht so richtig klappt. Ich denke Bug und kein Feature.

Edit1:
Ich hatte gerade noch n'bißchen über die automatische Typkonvertierung gegrübelt. War mir gar nicht so richtig im Klaren, wann das eigentlich stattfindet. Am Ende muß der Aufrufer sicherstellen, daß da 8 Byte übergeben werden. Nach Python Doku (2.5) representiert c_float abenso wie c_double ein C double. Dann sollte das eigentlich funktionieren.

Edit2:
Ab Python 2.6 ist c_float auch ein C float. Dann ist es wohl ein Feature, daß das nicht geht. Aber ob das wirklich Absicht ist? Hmm. Na ja wie gesagt, gut zu wissen.
lunar

@hendrikS: Vergiss Typkonvertierungen. Typkonvertierungen sind Sache des C-Compilers und geschehen während der Übersetzung des Quelltexts, nicht aber beim Aufruf einer Funktion zur Laufzeit. Zur Laufzeit gibt es nämlich keine Typen im Sinne der Sprache C mehr.

Insofern darf man sich bei der Verwendung von ctypes weniger an die C-Typen halten, sondern muss vielmehr Klarheit darüber haben, wie die übergebenen Argumente und der Rückgabewert in Assembler bzw. in der ABI der C-Funktion aussehen. C mag float und double implizit konvertieren (so wie viele andere Typen), doch binär sieht eine Fließkommazahl einfacher Genauigkeit eben anders aus als eine doppelter Genauigkeit.
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Jetzt frage ich mal ganz naiv: Sind Python-Floats im Prinzip äquivalent zum Datentyp Double in C? Wenn man C nicht wirklich gelernt hat, ist das nämlich erstmal recht verwirrend. Dazu bei trägt dann noch der schon angesprochene Umstand, dass zum Beispiel der GCC ebenfalls beim Kompilieren implizit umwandelt, sodass man seinen eigentlichen Fehler nicht bemerkt. Ich hatte bis gestern einen Double (und jetzt bitte nicht auslachen) noch für die Darstellung eines Wertes mit lediglich 2 Nachkommastellen gehalten (hätte ja sein können, dass der weniger Speicher verbraucht). :o :lol:
lunar

Mit Verlaub, aber wenn man "double" für eine Festkommazahl mit zwei Nachkommastellen hält, dann lässt man besser erst einmal die Finger von ctypes und lernt zumindest die absoluten Grundlagen von C. Dazu gehört gerade im Bezug auf ctypes eine gewisse Kenntnis über die Größen und die Binärdarstellungen der elementaren C-Datentypen.

In jedem Fall ist "float()" in Python nicht per definitionem äquivalent zu "double" in C, man kann sich also theoretisch nicht darauf verlassen. Praktisch gilt diese Äquivalenz auf allen drei Python-Implementierungen, denn jede dieser Plattformen rechnet letztlich wie auch C mit der Fließkommaeinheit der jeweiligen Architektur, da in Software implementierte Fließkommaarithmetik verhältnismäßig langsam ist.

Trotzdem sollte man bei ctypes immer die Typen der Funktion spezifizieren, damit ctypes entsprechend umwandeln kann.
Zuletzt geändert von lunar am Montag 29. November 2010, 19:02, insgesamt 1-mal geändert.
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

lunar hat geschrieben:Mit Verlaub, aber wenn man "double" für eine Festkommazahl mit zwei Nachkommastellen hält, dann lässt man besser erst einmal die Finger von ctypes und lernt zumindest die absoluten Grundlagen von C. Dazu gehört gerade im Bezug auf ctypes eine gewisse Kenntnis über die Größen und die Binärdarstellungen der elementaren C-Datentypen.
Ich muss im Nachhinein wie gesagt über mich selbst lachen. Damit, dass mir Elementarwissen in der Hinsicht fehlt, hast du natürlich völlig recht.
Antworten