Frage von Marvin091, 103

Aus zwei Integer(a=21, b=92) ein float machen(21.92) wie genau?

Hallo Leute, Ich habe mal ne Frage und zwar habe ich zwei Integers. Und möchte daraus gerne ein Float machen also ein Kommazahl. int a gibt in diesen Fall denn vorderen Wert vorm Komma an und b hinterm Komma. Programmieren tut ich in C.

Wie kann ich dieses man besten machen?

Wenn int a 21 ist und int b 29 ist das draus 21.29 Float wird?

Mfg Marvin.

Expertenantwort
von TeeTier, Community-Experte für programmieren, 20

Da sich in den anderen Antworten die üblichen Verdächtigen kloppen (ralphdieter, Willibergi, ceevee ^^) will ich auch mitmachen! (siehe dazu das Bild im Anhang!!!) :)

Hier mal etwas älterer Code, für zufällig die identische Aufgabenstellung (leicht an die vorliegende Frage angepasst):

http://pastebin.com/AmVSJBMi

Zum verlinkten Code gibt es eigentlich nicht mehr viel zu sagen, außer dass es sich - wie in der Fragestellung gefordert - um C++ handelt.

Zusammenfassung: Für Leute, die nicht den ganzen Code lesen wollen hier die verstrichene Zeit, für jeweils 10 Mio. Durchläufe (static bedeutet auf statische Werte optimiert, dynamic bedeutet überraschenderweise das Gegenteil ^^):

  • static 0.01 / dynamic 0.04 constant: Meine Eigenentwicklung als constexpr (wird - wenn möglich - zur Übersetzungszeit berechnet und hat Null Laufzeit-Overhead; wäre mit Java z. B. gar nicht möglich)
  • static 0.01 / dynamic 1.00 log: Die Logarithmus-Methode aus ralphdieters und Willibergis Code.
  • static 0.01 / dynamic 0.04 mul: Das war ursprünglich die div-Methode (n / 10), habe daraus aber mul (n * 0.1) gemacht. Ist im Endeffekt aber das Gleiche.
  • static 8.66 / dynamic 8.66 str::a: String Methode A. Einfach beide Teile aneinander gehängt, und das Ergebnis als Gleitpunktzahl geparst. Sehr dämlich und dementsprechend ineffizient.
  • static 6.52 / dynamic 6.45 str::b: Die zweite String Methode, die wieder aus dem Code bisheriger Antworten abgeleitet ist. Zwar besser als String Methode A, aber überhaupt kein Vergleich mit den anderen.

Fazit: Die Verwendung von Strings jeglicher Art sollte man tunlichst unterlassen. Zumindest im Hinblick auf Effizienz.

Bei Statischen Werten nehmen sich die ersten drei Methoden nicht viel, aber bei dynamischen Werten kann der Compiler die Methoden aus der Mathebibliothek nicht mehr optimieren, und dementsprechend  verheerend sieht dann auch die Logarithmus-Methode aus.

Meine Methode (constant) und die Multiplikationsmethode (mul) liegen gleich auf, und bei Letzterer vermute ich, dass es der guten Code-Optimierung durch den Compiler geschuldet ist. :)

Für diese beiden letzten habe ich die Anzahl der Durchläufe nochmal auf eine Milliarde erhöht, und meine constexpr-Methode benötigt 4.04 Sekunden. Die Multiplikations-Methode hingegen 4.37 Sekunden.

Das hängt natürlich auch immer stark von Plattform und Compiler ab, aber im Schnitt würde ich sagen, dass die Constexpr-Methode die schnellste ist. Allerdings braucht man dafür einen C++11 fähigen Compiler! :)

Schönen Abend noch! :)

PS: Ich wette, irgendjemand findet wieder irgendwelche vermeidbaren Logikfehler in meinem Code. ><

Kommentar von TeeTier ,

Mir ist heute früh noch eine kleinere Verbesserung eingefallen:

namespace fast {

namespace {

template <typename I=long>
constexpr I scale(const I post, const I fac=1) {
return post > fac ? scale<I>(post, fac * 10) : fac;
}

} // helper

template <typename F=double, typename I=long>
constexpr F concat(const I &pre, const I &post) {
return pre + static_cast<F>(post) / scale<I>(post);
}

} // namespace fast

Um beim optischen Eindruck meiner obigen Antwort zu bleiben:

  • static 0.01 / dynamic 0.03 fast: Das gilt wie gesagt für 10 Mio. Durchläufe, was ziemlich an der Grenze des Messbaren kratzt.

Bei einer Milliarde Durchläufen hingegen lande ich bei 3.19 Sekunden, was nochmal deutlich mehr als ein ganzes Drittel besser ist, als die ursprüngliche Multiplikationsmethode. (Das wird dann wohl der Unterschied zwischen Gleitpunkt- und Ganzzahl-Arithmetik sein.)

Sonderlich viel mehr Optimierungspotential erkenne ich hier ehrlich gesagt nicht mehr. Was für eine nette Spielerei!

Gut, dass wir dieses wichtige Thema mal geklärt haben! :)

Kommentar von ralphdieter ,

Ein Bug (Juhuuuuh!):  return post >= fac ? ...

Kommentar von TeeTier ,

Ach, verdammt ... sowas passiert halt, wenn man auf ordentliche Tests verzichtet. ><

Ist zwar nur eine Spielerei auf GF, aber selbst hier ist ausgiebiges Testen wichtig.

Das nächste mal werde ich dann zu meinen Snipptets eine Testsuite schreiben, die Dokumentation veröffentlichen, und einen Bugtracker einrichten. :)

Kommentar von ralphdieter ,

PS: Ich wette, irgendjemand findet wieder irgendwelche vermeidbaren Logikfehler in meinem Code.

Die Wette hast Du verloren. Und ich habe mich wirklich bemüht :-(

Dein Code tut und produziert bei mir etwa die gleichen Ergebnisse (bis auf einen Faktor). Nur 'constant' und 'mul' sollten eigentlich dasselbe tun. Mein g++-4.8 hat aber offenbar mit dem rekursiven Template seine Probleme. Die mul-Schleife ist bei mir doppelt so schnell wie 'constant'. Aber da ja C-Code gefragt ist, lasse ich die Templates mal außen vor und fasse zusammen:

  • STR: n / 10^(len(String(n))) — 30s bis 200s (threadsafe)
  • LOG: n / 10^(log(n)+1)         — 12s
  • DIV: while (n>=1) {n /= 10}    — 5.5s
  • MUL: while (n>=1) {n *= 0.1} — 1.3s
  • INV: p=1; while (p<=n) {p*=10}; ⇒ n /= p — 0.65s

STR ist plump, umständlich, langsam, hat evtl. Probleme mit Tausender-Trenner und ist schwer auf andere Basen anpassbar.

LOG ist flexibler und schneller, macht aber teure Berechnungen.

DIV, MUL: Eine Schleife ist deutlich schneller als pow(10,x). Der Unterschied zwischen /=10 und *=0.1 überrascht mich dabei sehr.

INV: Deine "kleine Verbesserung" ist in Wahrheit ein neuer Algorithmus, der das kleinste 10^x über n sucht und dann n durch diese Zahl teilt. Das gibt garantiert keine Rundungsprobleme und ist bei mir sogar signifikant schneller.

Fazit: Mit MUL hast Du meinen Rekord (DIV) um den Faktor 4 geschlagen, und dies mit dem Nachschlag (INV) nochmal verdoppelt. So nebenbei hast Du auch das Thema Rechengenauigkeit abgehakt. Ich gebe mich geschlagen.

Aber LOG ist trotzdem am elegantesten :-P

Kommentar von TeeTier ,

Der große Vorteil von constexpr ist ja auch, dass alles - soweit möglich - zur Kompilierzeit ausgewertet wird, und i2f(123, 456) direkt in eine Gleitpunktzahl übersetzt wird, so als hätte man direkt 123.456 als Literal im Code stehen. (Der gesamte Funktionsaufruf resultiert dann in einer einzigen MOV-Instruktion!)

Dass mein Algorithmus anders funktioniert ist mir auch aufgefallen, als ich nach einem passenden Funktionsnamen gesucht habe, weil "scale()" ja eigentlich unpassend dafür ist. Aber egal. :)

Aber LOG ist trotzdem am elegantesten :-P

LOG ist ehrlich gesagt auch die Lösung, die ich bei nicht-zeitkritischen Anwendungen in der Realität vorziehen würde. :)

Antwort
von Willibergi, 58

In Java:

int a = 21, b = 92;

float c = a + b/100.0;

In c steht nun 21,92. ^^

Ich hoffe, ich konnte dir helfen; wenn du noch Fragen hast, kommentiere einfach. 

LG Willibergi 

Kommentar von ceevee ,

Prinzipiell richtig, allerdings brauchst du zusätzlich noch eine Funktion, um die Stellenzahl einer Zahl zu ermitteln. Damit du in deinem Fall weißt, dass du durch 100 und nicht durch 10 oder 1000 oder eine andere Zehnerpotenz teilen musst. :)

Kommentar von Willibergi ,

Stimmt natürlich. ^^

Korrekt wäre:

int a = 21, b = 92;

float c = a + (float)b/Math.pow(10, Integer.parseInt(b.toString().length());

Danke für die Korrektur! ;)

LG Willibergi

Kommentar von ralphdieter ,

b.toString().length()

Schande über Dich! Wieviele Ziffern hat n zur Basis b? Richtig: 1+log_b(n), abgerundet. Welchen Beruf hast Du nochmal?

Um zu demonstrieren, wie ineffizient der Umweg über String ist, habe ich es mal programmiert. Im Kern steht die Funktion:

  /** @param: n = a positive number
* @param func = 'str', 'log', or 'div' (intern!)
* @return the decimal digits of n as a fraction (0.1 to 0.999...)
*/
static double toFrac ( int n, String func ) throws Error
{
switch ( func )
{
case "str":
return n / pow( 10, String.valueOf(n).length() );
case "log":
return n>0 ? n / pow(10, floor(log10(n))+1) : 0;
case "div":
double d=n; while (d>=1) d/=10; return d;
default:
throw new Error("Unsupported function '%s'. ('log' or 'str' expected)");
}
}

Ergebnis: Mein altersschwacher Pentium 4 braucht für floor(log()) fast genauso lang wie für String.valueOf() (allerdings mit deutlich weniger Speicher). Mit "div" (/10 sooft wie nötig) geht alles viermal schneller (vermutlich ist pow() auch recht teuer).

Auch in C gewinnt "div" das Rennen. "str" kostet hier fast doppelt so viel wie "log". Hier mein Code (für den Fragesteller):

static double toFrac ( int n, char const* func )
{
switch ( func[0] )
{
case 's': // "str":
{
static char text[20];
sprintf( text, "%d", n );
return n / pow( 10, strlen(text) );
}
case 'l': // "log":
return n>0 ? n / pow(10, (int)log10(n)+1) : 0;
case 'd': // "div":
{
double d=n; while (d>=1) d/=10; return d;
}
default:
fprintf(stderr, "Unsupported function '%s'. ('log' or 'str' expected)", func);
abort();
}
}

Der fettgedruckte Code scheint wohl die billigste Variante zu sein :-/

Kommentar von Willibergi ,

"Welchen Beruf hast Du nochmal?"

Um mich der Schande zu entziehen, sag' ich einfach mal: Deutschlehrer. Ich hab' ja Auswahl. ^^

Nein, im Ernst: Du magst zwar vielleicht Recht haben, dass der von dir vorgeschlagenen Code effizienter und weniger speicherintensiv ist.

Allerdings ist es bei den heutigen Rechnerarchitekturen nicht mehr wirklich wichtig, den Code so speicherschonend wie möglich zu schreiben. Genauso wird heutzutage nicht mehr darauf geachtet, welchen Wertebereich eine Ganzzahl haben muss. Meistens wird ein Integer verwendet, auch wenn ein Short oder sogar ein Byte reichen würde (z. B. bei Zählvariablen von Schleifen).

So ist es auch bei der Programmierung von derart kleinen Funktionen. Es ist nicht ungeheuerlich wichtig, auf jede kleine Effizienz- und Speicheroptimierung zu achten.

Bei den heutigen Prozessoren ist es wichtiger, dass der Code lesbar und verständlich ist. ;)

LG Willibergi

Kommentar von ralphdieter ,

Hallo Deutschlehrer :)

mir ging's in erster Linie nur um das Verfahren, weil es eleganter ist, nicht von Sprach-Einstellungen abhängt und z.B. leicht auf andere Basen verallgemeinert werden kann. Dass es auch wesentlich effizienter ist, hat sich ja (in Java) nicht wirklich gezeigt.

Es ist nicht ungeheuerlich wichtig, auf jede kleine Effizienz- und Speicheroptimierung zu achten.

Ja, mit Betonung auf "kleine" :-) Aber leider höre ich dieses Argument zu oft von Programmieren, die lineare und quadratische Laufzeit nicht unterscheiden können. Und ich denke, dass man am besten klein anfängt und für überschaubare Probleme mehrere Ansätze ausprobiert. So bekommt man ein Gefühl dafür, wie ein Prozessor (oder eine VM) tickt.

Deshalb habe ich oben auch alle Code-Varianten demonstriert, nicht nur die (nach meinem Ermessen) beste. So was kostet zwar etwas Zeit, aber bisher habe ich das noch kein einziges Mal bereut.

Auch jetzt habe ich dazugelernt: Das String-Management ist in Java offenbar doch nicht so katastrophal wie ich gedacht habe. Für 100 Millionen erzeugte Strings a 4 Zeichen wurden "nur" 73 MB Speicher verbraten — ich hätte ein Zigfaches davon erwartet.

Kommentar von TeeTier ,

Du bist immer so schön kritisch! Was sagst du denn zu dem Code aus meiner Antwort? :)

Übrigens sind Benchmarks mit der VM etwas, wobei man sich leicht in den Fuß schießen kann! Du schreibst, dass 100 Mio. Strings 73 Mio. Byte verbrauchen ... dann ist Java mit 0.73 Byte pro String aber extrem effizient. Mit anderen Worten verbraucht ein String dann ja nur 5.84 Bit. Respekt! Ich hätte auch nicht gedacht, dass Java so effizient sein kann! :)

Ach so, und ... Wenn Willibergi "nur" Deutschlehrer ist, dann bin ich auch "nur" Künstler. :)

Kommentar von ralphdieter ,

dann ist Java mit 0.73 Byte pro String aber extrem effizient

oder muss den GC anschmeißen :)

Antwort
von Jonas711, 34

Geht es dir jetzt um C (wie im Fragetext) oder um C++ (Tag)?

In C wäre der Code wie folgt:

#include <stdio.h>

#include <math.h>

int main(void) {

int a = 21;
int b= 92;

char b_string[15];
sprintf(b_string, "%d", b);
int length = strlen(b_string);
float factor = pow(10,length);

float result = a+b/factor;

printf("%f",result);

return 0;
}

Keine passende Antwort gefunden?

Fragen Sie die Community