ANSI-C: maximale Anzahl von Zeichen, die eine Dezimalzahl ausgeben

8

Ich würde gerne wissen, ob es eine einfache Möglichkeit ist, die maximale Anzahl von Zeichen zu bestimmen, um eine Dezimalzahl int zu drucken.

Ich weiß, <limits.h> enthält Definitionen wie INT_MAX , die den maximalen Wert angeben, den ein int annehmen kann, aber das ist nicht das, was ich will.

Ich würde gerne etwas tun können wie:

%Vor%

Aber wie findet man den Wert von MAX_CHAR_OF_A_DECIMAL_INT in einer tragbaren und wenig übersättigten Weise?

Danke!

    
j4x 10.05.2012, 14:29
quelle

6 Antworten

2

Ich weiß nicht, ob es irgendeinen Trick gibt, was Sie in ANSI-C machen wollen, aber in C ++ können Sie das Template-Metaprogrammieren einfach machen:

%Vor%

Und Sie können es aus Ihrem pure-C-Code aufrufen, indem Sie eine zusätzliche C ++ - Funktion wie folgt erzeugen:

%Vor%

Dies hat einen Zeitaufwand für die ZERO-Ausführung und berechnet den genauen Platzbedarf.

Sie können die obigen Vorlagen mit etwas wie:

testen %Vor%

Die Ausgabe ist:

%Vor%
  • Beachten Sie die geringfügig abweichenden Werte von std::numeric_limits< T >::digits10 und MaxLen & lt; T, N & gt; :: StringLen, da ersterer Zahlen nicht berücksichtigt, wenn if '9' nicht erreichen kann. Natürlich können Sie es verwenden und einfach zwei hinzufügen, wenn Sie in einigen Fällen kein einziges Byte verschwenden.

BEARBEITEN:

Einige mögen seltsam gefunden haben, einschließlich <climits> . Wenn Sie mit C ++ 11 rechnen können, werden Sie es nicht benötigen und eine zusätzliche Einfachheit erhalten:

%Vor%

Jetzt können Sie

verwenden %Vor%

statt

%Vor%

Gut, nicht?

    
JaxWR 10.05.2012, 17:24
quelle
9

Wenn Sie annehmen, CHAR_BIT ist 8 (erforderlich für POSIX, also eine sichere Annahme für jeden Code, der sowohl auf POSIX-Systeme als auch auf andere gängige Systeme wie Windows abzielt), ist eine billige sichere Formel 3*sizeof(int)+2 . Wenn nicht, können Sie es 3*sizeof(int)*CHAR_BIT/8+2 machen, oder es gibt eine etwas einfachere Version.

Wenn Sie sich für den Grund interessieren, ist sizeof(int) im Wesentlichen ein Logarithmus von INT_MAX (grob logarithmiert 2 ^ CHAR_BIT), und die Konvertierung zwischen Logarithmen verschiedener Basen (zB zur Basis 10) ist einfach Multiplikation. Insbesondere ist 3 eine ganzzahlige Annäherung / obere Grenze auf der logarithmischen Basis 10 von 256.

Die +2 soll ein mögliches Vorzeichen und eine Null-Beendigung berücksichtigen.

    
R.. 10.05.2012 14:31
quelle
2

Der einfachste und wohl portabelste Weg ist es, snprintf() zu fragen, wie viel Speicherplatz benötigt würde:

%Vor%

etwas weniger portabel, vielleicht mit intmax_t und %j :

%Vor%

Man könnte meinen, dass es zu teuer ist, dies zur Laufzeit zu tun, aber es kann für jeden Wert arbeiten, nicht nur für die MIN / MAX-Werte eines Integer-Typs.

Sie könnten natürlich auch direkt die Anzahl der Stellen berechnen, die eine gegebene ganze Zahl benötigen würde, um in der Basis-10-Notation mit einer einfachen rekursiven Funktion ausgedrückt zu werden:

%Vor%

aber das erfordert natürlich auch die CPU zur Laufzeit, auch wenn sie inline ist, obwohl vielleicht etwas weniger als snprintf() .

@ R.'s obige Antwort ist mehr oder weniger falsch, aber auf dem richtigen Weg. Hier ist die korrekte Herleitung einiger sehr gut und weit getesteter und sehr portabler Makros, die die Berechnung zur Kompilierungszeit implementieren, indem sizeof() verwendet wird, wobei eine leichte Korrektur von @ Rs anfänglicher Formulierung verwendet wird:

Zuerst können wir leicht sehen (oder zeigen), dass sizeof(int) die Log-Basis 2 von UINT_MAX geteilt durch die Anzahl der Bits ist, die durch eine Einheit sizeof() (8, aka CHAR_BIT ) dargestellt werden:

sizeof (int) == log2 (UINT_MAX) / 8

weil UINT_MAX natürlich nur 2 ^ (sizeof (int) * 8)) und log2 (x) die Umkehrung von 2 ^ x ist.

Wir können die Identität "logb (x) = log (x) / log (b)" verwenden (wobei log () der natürliche Logarithmus ist), um Logarithmen anderer Basen zu finden. Zum Beispiel könnten Sie die "log base 2" von "x" mit:

berechnen

log2 (x) = log (x) / log (2)

und auch:

log10 (x) = log (x) / log (10)

Also können wir folgern:

log10 (v) = log2 (v) / log2 (10)

Nun, was wir am Ende wollen, ist die Log-Base 10 von UINT_MAX , also da log2 (10) ungefähr 3 ist, und da wir von oben wissen, was log2 () in sizeof() ist, können wir sagen wir, dass log10 ( UINT_MAX ) ungefähr:

ist

log10 (2 ^ (sizeof (int) * 8)) ~ = (sizeof (int) * 8) / 3

Das ist aber nicht perfekt, vor allem, weil wir wirklich den Höchstwert wollen, aber mit ein wenig Anpassung für die Ganzzahlrundung von log2 (10) auf 3 können wir bekommen, was wir brauchen, indem wir zuerst eins zu den Log2-Term, dann Subtrahieren von 1 aus dem Ergebnis für eine größere Ganzzahl, was zu diesem "Gut genug" -Ausdruck führt:

%Vor%

Noch besser können wir unseren ersten log2 () Term mit 1 / log2 (10) multiplizieren (Multiplikation mit dem Reziproken des Divisors ist das gleiche wie Division durch den Divisor), und dies macht es möglich, eine bessere ganze Zahl zu finden Annäherung. Ich bin kürzlich (re?) Auf diesen Vorschlag gestoßen, als ich Sean Anderson's Bithacks gelesen habe: Ссылка

Um dies mit ganzzahliger Mathematik in der bestmöglichen Annäherung zu erreichen, müssen wir das ideale Verhältnis finden, das unser reziprokes darstellt. Dies kann gefunden werden, indem nach dem kleinsten Bruchteil der Multiplikation unseres gewünschten Wertes von 1 / log2 (10) mit aufeinanderfolgenden Potenzen von 2 innerhalb eines vernünftigen Bereichs von Potenzen von 2 gesucht wird, wie mit dem folgenden kleinen AWK-Skript:

%Vor%

So können wir eine gute ganzzahlige Annäherung erhalten, indem wir unseren log2 (v) -Wert mit 1 / log2 (10) multiplizieren, indem wir ihn mit 1233 multiplizieren, gefolgt von einer Rechtsverschiebung von 12 (2 ^ 12 ist natürlich 4096):

log10 (UINT_MAX) ~ = ((sizeof (int) * 8) + 1) * 1233 & gt; & gt; 12

und, zusammen mit dem Hinzufügen von einem, um das Äquivalent zum Finden des Höchstwerts zu machen, das beseitigt die Notwendigkeit, mit ungeraden Werten herumzuspielen:

%Vor%

während normalerweise der Compiler zur Kompilierzeit den Ausdruck auswertet, den mein __MAX_B10STRLEN_FOR_INT_TYPE() Makro wird. Natürlich berechnet mein Makro immer den maximalen Platz, der von einem gegebenen Integer-Typ benötigt wird, nicht den genauen Platz, der von einem bestimmten Integer-Wert benötigt wird.

    
Greg A. Woods 24.11.2012 22:55
quelle
2

Nach Annahme der Antwort (2+ Jahre)

Der folgende Teil 10/33 entspricht exakt den Anforderungen für nicht gepolsterte int8_t , int16_t , int32_t und int128_t . Nur 1 char extra für int64_t . Exact oder 1 over für alle Integer-Größen bis int362_t . Darüber hinaus kann mehr als 1 sein.

%Vor%

** fgets() funktioniert in der Regel am besten mit einem zusätzlichen char für das abschließende '\n' .

Ähnlich wie @R .. , aber mit einem besseren Bruchteil.

Empfehlen Sie beim Lesen von Benutzereingaben die Verwendung von großzügigen, doppelten Puffern. Manchmal fügt ein Benutzer Leerzeichen, führende Nullen usw. hinzu.

%Vor%     
chux 26.09.2014 20:46
quelle
1

Hier ist die C-Version:

%Vor%

Dann:

%Vor%     
Alun 25.08.2016 23:40
quelle
0

Sie können die Anzahl der Ziffern mit log base 10 berechnen. In meinem System führte die Berechnung der Obergrenze von log base 2 unter Verwendung der Bitdarstellung der Zahl zu keinem signifikanten Geschwindigkeitsgewinn. Der Boden von log base 10 + 1 gibt die Anzahl der Ziffern an, ich füge 2 hinzu, um das Nullzeichen und das Zeichen zu berücksichtigen.

%Vor%

Beachten Sie auch, dass die Anzahl der Bytes eines int 2 oder 4 sein kann und es nur in alten Systemen 2 ist. Sie könnten also die obere Grenze berechnen und in Ihrem Programm verwenden.

    
user3233318 03.05.2015 12:20
quelle

Tags und Links