wake-up-neo.net

Was ist der richtige Weg, um mit printf eine clock_t zu drucken?

Ich verwende derzeit eine explizite Umwandlung in unsigned long long und verwende %llu, um es zu drucken. Da jedoch size_t den %z-Bezeichner hat, warum hat clock_t keinen?

Es gibt nicht einmal ein Makro dafür. Vielleicht kann ich davon ausgehen, dass size_t auf einem x64-System (Betriebssystem und CPU) 8 Byte lang ist (und sogar in diesem Fall haben sie %z bereitgestellt), aber was ist mit clock_t?

29
Spidey

Es scheint keinen perfekten Weg zu geben. Die Ursache des Problems ist, dass clock_t entweder eine Ganzzahl oder ein Gleitkomma sein kann.

clock_t kann ein Gleitkommatyp sein

Wie von Bastien Léonard erwähnt für POSIX (geht auf ihn hinaus), C99 N1256 Draft 7.23.1/3 sagt auch:

[clock_t is] arithmetische Typen, die Zeiten darstellen können

und 6.2.5/18:

Ganzzahlige und gleitende Typen werden zusammen als Arithmetiktypen bezeichnet.

und der Standard definiert den arithmetischen Typ entweder als Ganzzahl oder als Gleitkommatyp.

Wenn Sie durch CLOCKS_PER_SEC teilen, verwenden Sie long double.

Der Rückgabewert von clock() ist implementierungsdefiniert. Die Standardbedeutung kann nur durch die Division durch CLOCKS_PER_SEC erreicht werden, um die Anzahl der Sekunden zu ermitteln:

clock_t t0 = clock();
/* Work. */
clock_t t1 = clock();
printf("%Lf", (long double)(t1 - t0));

Das ist gut genug, wenn auch nicht perfekt, aus zwei Gründen:

  • es scheint kein Analogon zu intmax_t für Fließkommatypen zu geben: Wie erhalte ich den Fließkommadatentyp mit der höchsten Genauigkeit und seinen printf-Spezifizierer? Wenn also morgen ein größerer Fließkommatyp herauskommt, könnte es sein verwendet und brechen Sie Ihre Implementierung.

  • wenn clock_t eine ganze Zahl ist, ist die Umwandlung in Float so definiert, dass der nächstliegende Float verwendet wird. Sie verlieren möglicherweise die Genauigkeit, aber im Vergleich zum absoluten Wert ist dies nicht von Bedeutung und würde nur für sehr lange Zeit geschehen, z. long int in x86 ist der 80-Bit-Float mit 64 Bit, was Millionen von Jahren in Sekunden ist.

Go upvote lemonad der etwas ähnliches gesagt hat.

Wenn Sie annehmen, dass es eine ganze Zahl ist, verwenden Sie% ju und uintmax_t.

Obwohl unsigned long long derzeit der größte mögliche Standardwert ist:

daher ist es am besten, den größtmöglichen vorzeichenlosen Integer-Typ zu konvertieren:

#include <stdint.h>

printf("%ju", (uintmax_t)(clock_t)1);

uintmax_t hat garantiert die Größe der größtmöglichen Ganzzahl auf der Maschine.

uintmax_t und sein printf-Spezifizierer %ju wurden in c99 eingeführt, und gcc implementiert sie zum Beispiel.

Als Bonus löst dies ein für alle Mal die Frage, wie zuverlässig printf Integer-Typen (was leider nicht unbedingt der Fall für clock_t ist).

Was könnte schief gehen, wenn es ein Double wäre:

  • wenn es zu groß ist, um in die Ganzzahl zu passen, undefiniertes Verhalten
  • viel kleiner als 1, wird auf 0 gerundet und Sie sehen nichts

Da diese Konsequenzen viel härter sind als die Umwandlung von Ganzzahl in Float, ist die Verwendung von Float wahrscheinlich eine bessere Idee.

Auf glibc 2.21 ist es eine ganze Zahl

Das Handbuch besagt, dass die Verwendung von double eine bessere Idee ist:

Auf GNU/Linux- und GNU/Hurd-Systemen entspricht clock_t einem long int und CLOCKS_PER_SEC ist ein ganzzahliger Wert. In anderen Systemen können clock_t und das Makro CLOCKS_PER_SEC jedoch entweder Ganzzahl- oder Gleitkommatypen sein. Durch das Verdoppeln der CPU-Zeitwerte (wie im obigen Beispiel) wird sichergestellt, dass Vorgänge wie Arithmetik und Drucken ordnungsgemäß und konsistent funktionieren, unabhängig von der zugrunde liegenden Darstellung. 

In Glibc 2.21:

Siehe auch

Dies liegt wahrscheinlich daran, dass die Uhrenzähler keine sehr gut definierte Einheit sind. Sie können es in Sekunden konvertieren und es als Doppelter ausgeben:

time_in_seconds = (double)time_in_clock_ticks / (double)CLOCKS_PER_SEC;
printf("%g seconds", seconds);

Das Makro CLOCKS_PER_SEC wird zu einem Ausdruck erweitert, der die Anzahl der Taktstriche in einer Sekunde darstellt.

11
lemonad

Soweit ich weiß, ist das Beste, was Sie tun. Außer dass clock_t ein reeller Typ sein kann:

time_t und clock_t müssen vom Typ Integer oder Real Floating sein.

http://www.opengroup.org/onlinepubs/009695399/basedefs/sys/types.h.html

11

Der C-Standard muss eine Vielzahl von Architekturen aufnehmen, so dass neben der Tatsache, dass der interne Uhrentyp arithmetisch ist, keine weiteren Garantien möglich sind.

In den meisten Fällen interessieren Sie sich für Zeitintervalle, daher würde ich die Differenz der Uhr in Millisekunden umrechnen. Ein unsigned long ist groß genug, um ein Intervall von fast 50 Tagen darzustellen, auch wenn sein 32-Bit-Wert für die meisten Fälle groß genug ist:

clock_t start;
clock_t end;
unsigned long millis = (end - start) * 1000 / CLOCKS_PER_SEC;
3
Christoph

Eine Möglichkeit ist die Verwendung der Funktion gettimeofday. Man kann den Unterschied anhand dieser Funktion feststellen:

unsigned long  diff(struct timeval second, struct timeval first)
{
    struct timeval  lapsed;
    struct timezone tzp;
    unsigned long t;

    if (first.tv_usec > second.tv_usec) {
        second.tv_usec += 1000000;
        second.tv_sec--;
    }

    lapsed.tv_usec = second.tv_usec - first.tv_usec;
    lapsed.tv_sec  = second.tv_sec  - first.tv_sec;
    t = lapsed.tv_sec*1000000 + lapsed.tv_usec;

    printf("%lu,%lu - %lu,%lu = %ld,%ld\n",
           second.tv_sec, second.tv_usec,
           first.tv_sec,  first.tv_usec,
           lapsed.tv_sec, lapsed.tv_usec);

    return t;
}
1
rani