Michael Schröpl: Normalisieren von Gleitpunktzahlen (Stilfrage)

Hallo Leute,

ich möchte gerne in Perl Gleitpunktzahlen normalisieren:
a) rechtsstehende Nullen aus dem Nachkomma-Anzeil abtrennen und
b) einen danach überflüssigen Dezimalpunkt ebenfalls entfernen.

Mein Lösungsversuch

# Gleitpunkt-Darstellung normalisieren
  # (rechtsstehende Nachkomma-Nullen entfernen)
    if ($zahl =~ /^(\d+).(\d*?)0+$/)
       {
         my ($vorkomma, $nachkomma) = ($1, $2);
         if ($nachkomma) { $vorkomma .= '.'; }
         $zahl = $vorkomma . $nachkomma;
       }

scheint zwar zu funktionieren, kommt mir aber nicht arg 'perlish' vor.
Geht das nicht irgendwie noch deutlich eleganter?

Ach ja, das Ganze läuft als eine von vielen Teilfunktionen über eine
Datei mit 757 MB, und das Ganze ist Bestandteil eines täglich laufenden
Produktionsjobs - eine performante Lösung wäre mir also nicht unlieb. ;-)

Viele Grüße
      Michael

  1. Hallo,

    ich möchte gerne in Perl Gleitpunktzahlen normalisieren:
    a) rechtsstehende Nullen aus dem Nachkomma-Anzeil abtrennen und
    b) einen danach überflüssigen Dezimalpunkt ebenfalls entfernen.

    Ich gehe mal davon aus, das Du eine Bruchdarstellung von Integern wieder in "richtige" Integer umwandeln möchtest, ja?
    Hörte sich zumindest so an.

    Also z.B.:
    123.00 zu 123
    567567.0000000 zu 567567

    ?

    Mein Lösungsversuch

    # Gleitpunkt-Darstellung normalisieren
      # (rechtsstehende Nachkomma-Nullen entfernen)
        if ($zahl =~ /^(\d+).(\d*?)0+$/)
           {
             my ($vorkomma, $nachkomma) = ($1, $2);
             if ($nachkomma) { $vorkomma .= '.'; }
             $zahl = $vorkomma . $nachkomma;
           }

    scheint zwar zu funktionieren, kommt mir aber nicht arg 'perlish' vor.

    Das ist mit Sicherheit nicht "perlish", denn es ist gut lesbar, verständlich und ist nicht in einer Zeile.
    SCNR >;->

    Aber ich habe Dich wohl mißverstanden, denn der RegEx sagt mir sowas wie:

    123.1230000 zu 123123

    Geht das nicht irgendwie noch deutlich eleganter?

    Wenn es nur Nullen sind:

    $zahl = 123.000 # eine Zahl.
       if ($zahl =~ /^(\d+).0+$/)
          {
           $zahl = sprintf "$lu",$zahl;
          }

    (Aber sprintf ist erstens teuer, sagt perldoc und zweitens war das ja sowieso nicht gefragt ;-)

    $zahl = 123.123000;
        if ($zahl =~ /^(\d+).(\d*?)(0*)$/)
           {
            $zahl = $1 . $2;
           }
        print $zahl; # ergibt 123123,
                     # Test:
                     # mit $zahl = $1 . $2 . $3 ergibt es 123123000

    Wenn Du den Test nicht brauchst (also genau weißt, das es sich um eine Zahl handelt) reicht auch:

    $zahl =~ s/^(\d+).(\d*?)(0*)$/$1$2/;

    Das sieht dann wenigstens schon ansatzweise "perlish" aus. ;-)
    (Hoffentlich stimmt das auch alles, habe nur die Funktion geprüft, nicht auf Fehler oder gar irgendwelche Seiteneffekte)

    Ach ja, das Ganze läuft als eine von vielen Teilfunktionen über eine
    Datei mit 757 MB, und das Ganze ist Bestandteil eines täglich laufenden
    Produktionsjobs - eine performante Lösung wäre mir also nicht unlieb. ;-)

    Tja, das wäre dann noch zu testen. Zumindest wären zwei Variablen gespart und eine Prüfung, im zweitem Fall sogar noch eine Prüfung.
    Aber ich bin nicht der Perlspezialist und eigentlich bin ich ja auch nur aufgestanden, weil die Blase voll war ;-)

    Da sind doch mit Sicherheit ein paar mathematische Funktionen in Perl, oder? Wenn es ein Float ist, also eine reguläre Zahl, sollte ein wenig Exponentialrechnung eigentlich besser behilflich sein. Wenn Modulo Floats unterstützt wäre es auch einfacher.

    so short

    Christoph Zurnieden

    1. Hallo Christoph,

      Ich gehe mal davon aus, das Du eine Bruchdarstellung von Integern
      wieder in "richtige" Integer umwandeln möchtest, ja?

      nein - es sind keine Integers (jedenfalls nicht immer).

      if ($nachkomma) { $vorkomma .= '.'; }

      Deshalb hängt ich an $vorkomma ja auch den '.' an, falls in
      $nachkomma noch etwas Signifikantes übrig geblieben ist (und nur dann).

      123.1230000 zu 123123

      Nicht ganz. ;-)
      Den Dezimalpunkt brauche ich durchaus noch - jedenfalls manchmal.

      Geht das nicht irgendwie noch deutlich eleganter?

      (Aber sprintf ist erstens teuer, sagt perldoc und zweitens war das
      ja sowieso nicht gefragt ;-)

      "sprintf" habe ich durchaus auch probiert, aber ohne Erfolg.
      (Das ist mir zu "C-ish", um zu begreifen, wie ich das nutzen kann ...)

      Viele Grüße
            Michael

      1. Hallo,

        Ich gehe mal davon aus, das Du eine Bruchdarstellung von Integern
        wieder in "richtige" Integer umwandeln möchtest, ja?

        nein - es sind keine Integers (jedenfalls nicht immer).

        if ($nachkomma) { $vorkomma .= '.'; }

        Deshalb hängt ich an $vorkomma ja auch den '.' an, falls in
        $nachkomma noch etwas Signifikantes übrig geblieben ist (und nur dann).

        123.1230000 zu 123123

        Nicht ganz. ;-)
        Den Dezimalpunkt brauche ich durchaus noch - jedenfalls manchmal.

        Kein Problem, dann so:

        $zahl =~ s/^(\d+)(.)(\d*?)(0*)$/$1$2$3/;

        Kurzer Moment der Andacht, dann den hier:

        $zahl1 =~ s/^(\d+.\d*?)(0*)$/$1/;

        Geht das nicht irgendwie noch deutlich eleganter?

        (Aber sprintf ist erstens teuer, sagt perldoc und zweitens war das
        ja sowieso nicht gefragt ;-)

        "sprintf" habe ich durchaus auch probiert, aber ohne Erfolg.
        (Das ist mir zu "C-ish", um zu begreifen, wie ich das nutzen kann ...)

        Aber nicht "C-ish" genug, das ich da auf Anhieb mit klar kam ;-)

        so short

        Christoph Zurnieden

  2. Hallo Leute,

    ich möchte gerne in Perl Gleitpunktzahlen normalisieren:
    a) rechtsstehende Nullen aus dem Nachkomma-Anzeil abtrennen und
    b) einen danach überflüssigen Dezimalpunkt ebenfalls entfernen.

    hm, hab Perl leider sehr wenig ahnung, aber ich kann sagen wie ich es in PHP machen würde (müsste übertragbar sein).

    Also, wenn ich dich richtig verstehe soll

    1982.2900 zu 1982.29
    7733.0000 zu 7733 werden

    mein Vorschlag in PHP

    $Zahl = strrev(strrev($Zahl));

    die Variable einfach 2x umdrehen hat genau den gewünschten effekt. kann zwar nicht wirklich sagen wie performant das ist, dürfte aber schneller als ein regex sein.

    gruss

    Thorsten S.

    1. Hallo,

      Also, wenn ich dich richtig verstehe soll

      1982.2900 zu 1982.29
      7733.0000 zu 7733 werden

      Also, so hab ich's auch verstanden.

      mein Vorschlag in PHP

      $Zahl = strrev(strrev($Zahl));

      In Perl geht das noch einfacher:
         $zahl = $zahl + 0;

      Beispiel:

      $zahl = '12.3400';
         $zahl = $zahl + 0;

      print $zahl;

      Ergebnis: 12.34

      Wäre $zahl = "12.000", würde "12" ausgegeben werden.

      Aber die Lösung erscheint mir so trivial, daß bestimmt was an der
      Fragestellung falsch verstanden wurde, oder?

      Gruß
      Slyh

      1. Hallo nochmal,

        Aber die Lösung erscheint mir so trivial, daß bestimmt was an der
        Fragestellung falsch verstanden wurde, oder?

        Nachdem ich darauf aufmerksam gemacht wurde, daß dieser Satz ausgesprochen
        unglücklich formuliert ist, möchte ich kurz darauf hinweisen, daß dieser
        Satz in keiner Weise abwertend gemeint ist/war. Er soll tatsächlich nur das
        ausdrücken, was dort wörtlich steht: Nämlich die Vermutung, daß ich die
        Fragestellung möglichweise falsch verstanden habe.
        Ich kenne Michael Schröpl durch stets hochkompetente Postings. Schon alleine
        deshalb würde mir eine solche niederschmetternde Wertung, die man zugegebener-
        maßen in den zitierten Satz hineininterpretieren könnte, nicht im Traum
        einfallen.

        Gruß
        Slyh

        1. Hallo Slyh,

          Ich kenne Michael Schröpl durch stets hochkompetente Postings.

          ;-)

          Aber Perl kann ich halt nicht annähernd richtig ... nochmal: Danke!

          Viele Grüße
                Michael

      2. Hallo Slyh,

        In Perl geht das noch einfacher:
           $zahl = $zahl + 0;

        ach je - Perl, die Sprache ohne Datentypen ...

        Warum komme ich eigentlich nicht auf das Naheliegende?
        (Wahrscheinlich, weil der Feldwert, den ich eigentlich zu analysieren
        habe, zusätzlich auch noch die Zeichen ' :dn' enthält, die ich per
        vorherigem regexp allerdings bereits entfernt hatte ...)

        Mit dem Brett vor meinem Kopf werde ich mir heute abend ein gemütliches
        Kaminfeuer machen. :-)

        Danke schön!

        Viele Grüße
              Michael