php-resource




Archiv verlassen und diese Seite im Standarddesign anzeigen :
Währungen mit Platzhalter runden


 
Koda
09-10-2015, 14:55 
 
Guten Tag

Ich versuche gerade eine Lösung zu finden um verschiedene Währungen von einer Hauptwährung EUR zu berechnen und zu runden.

Was ich bisher habe:
Tabelle währung:
ID | waehrung | waehrungName | exchangeRate | decimals | decPoint | thousandsSep
1 | EUR | Euro | 1.0000 | 2 | , | ''
1 | CHF | Schweizer Franken | 0.9876 | 2 | . | '
1 | ISK | Isländische Kronen | 102.12345 | 0 | '' | .Beschreibung:
exchangeRate = Wechselkurs
decimals = Nachkommastellen für die Funktion number_format
decPoint = Kommatrennzeichen für die Funktion number_format
thousandsSep = Tausendertrennzeichen für die Funktion number_format

Das ist soweit kein Problem. Ich habe einen Preis in EUR, und rechne diesen Preis x exchangeRate.

Nun suche ich aber eine Möglichkeit eine Spalte zu machen "runden" oder sowas.

Gerne würde ich eine Funktion machen damit ich nicht immer etwas in der Art haben muss wie:
if($waehrung == 'EUR') //runde auf diese weise
elseif($waehrung == 'CHF') //runde auf diese weise
elseif($waehrung == 'ISK') //runde auf diese weiseEs ist ja so, dass EUR Preise 2 Nachkommastellen haben ohne das diese gerundet werden müssen da es den Betrag 99.99 gibt. Bei Schweizer Franken gibt es zwei Nachkommastellen aber es muss auf .05 gerundet werden da nur noch 5 Rappen Stücke als kleinste Wärhung verwendet werden.
Bei ISK gibt es gar keine Nachkommastellen.
Jede währung ist da anders. Mal sind es mehr oder weniger Nachkommastellen und mal muss gerundet werden und mal nicht.

Hat da jemand eine Idee wie ich das sauber lösen könnte?

Vielen Dank schon im Voraus für jede Hilfe.

EDIT: Ich habe mir schon die Funktion money_format angeschaut erhalte aber immer das selbe Format. Ich weiss auch noch nicht ob das wirklich die Lösung sein könnte.

Gruss

Koda

 
SysOp
09-10-2015, 16:26 
 
Für number_format hast du Einstellungen je Währung, was hindert dich, auch Werte für round anzulegen?
Das Ergebis ist dann
number_format(round($ZAHL, "DEINE_VORGABE"),"DEINE_EINSTELLUNGEN");

 
Koda
09-10-2015, 16:42 
 
Hi

Vielen Dank. Nur bin ich nicht sicher wie ich das Automatisieren könnte. Vielleicht mache ich es auch einfach zu kompliziert.

Diverse Tutorials um fürSchweizer Franken zu Runden sehen so aus:
round($preis / 5, 2) * 5
Nun könnt ich ja die 5 in die Datenbank schreiben. Aber wie sieht es denn aus wenn ich auf Cent genau runden möchte? Dann passt das mit der Division ja nicht oder?

Gruss

Koda

 
mermshaus
10-10-2015, 07:31 
 
Ich würde es als „Auflösung“ definieren im Kontext der Zielwährung und jeweils den kleinstmöglichen Wert angeben:

EUR: 0.01
ISK: 1.00
CHF: 0.05

(Anschaulich gesprochen: Das kleinste von deiner Anwendung gewünschte Geldstück der Währung.)

Dann bildest du für einen Eingabe-Zahlenwert, der der Auflösung entsprechend formatiert werden soll, die nächstkleinere und nächstgrößere gültige Darstellung.

<?php

function f($x, $resolution)
{
$w = floor($x / $resolution) * $resolution;
$y = ceil($x / $resolution) * $resolution;

return array($w, $x, $y);
}

var_dump(
implode(', ', f(123.456, 0.01)),
implode(', ', f(123.456, 1.00)),
implode(', ', f(123.456, 0.05))
);

// string(23) "123.45, 123.456, 123.46"
// string(17) "123, 123.456, 124"
// string(22) "123.45, 123.456, 123.5"

Und davon wählst du diejenige, die näher an der Eingabe liegt oder wie auch immer.

function g($x, $resolution)
{
$w = floor($x / $resolution) * $resolution;
$y = ceil($x / $resolution) * $resolution;

return ($x - $w < $y - $x) ? $w : $y;
}

var_dump(
g(123.456, 0.01),
g(123.456, 1.00),
g(123.475, 0.05)
);

// float(123.46)
// float(123)
// float(123.45)

Aufmerksame Leser stellen an der Stelle fest, dass die letzte Ausgabe (float(123.45)) eigentlich nicht richtig ist, weil die return-Zeile besagt, dass bei gleichem Abstand aufgerundet wird. Es müsste also float(123.5) sein. Woran liegt das? Die üblichen Ungenauigkeiten beim Rechnen mit Fließkommazahlen.

php > $x = 123.475; $resolution = 0.05;
php > $w = floor($x / $resolution) * $resolution;
php > $y = ceil($x / $resolution) * $resolution;
php > var_dump($w, $y);
float(123.45)
float(123.5)
php > var_dump($x - $w, $y - $x);
float(0.024999999999991)
float(0.025000000000006)

Wie behebt man das? Nicht mit Fließkommazahlen rechnen.

Eine Alternative ist die bc-Erweiterung, die leider nicht standardmäßig in PHP enthalten ist.

- PHP: BC Math - Manual (http://php.net/manual/en/book.bc.php)

 
Koda
11-10-2015, 10:50 
 
Hi

Vielen Dank mermshaus. Einfach nur geil. So einfach und doch so effektiv :)

Gruss

Oliver

 
SysOp
12-10-2015, 14:44 
 
Der Vollständigkeit halber, ich meine, dass man buchhalterisch erst das Ergebnis runden darf, schlage also z.B. sowas vor:

Euro auf Währung;

function rechne($euro, $in_waehrung, $kurs, $runden, $format_nachkomma=2)
{
// Kurs berechenen
$ergebnis = $euro*$kurs;

// genauigkeit
$x = round($ergebnis, $runden);

// formatiert
$y = number_format($ergebnis,$format_nachkomma,',','.');

// das Ergebnis ausgeben
$back['euro'] = $euro;
$back[$in_waehrung]=$x;
$back['formatiert'] = $y;

return $back;
}

// rechne(Euro, in welche Währung, Kurs, Genauigkeit zum rechnen, gerundeter Wert)
$ergebnis = rechne(1,'USD',1.13747,5,2);

echo '<pre>';
print_r($ergebnis);
echo '</pre>';

 
Koda
13-10-2015, 17:21 
 
Hi

Vielen Dank SysOp. Das habe ich noch gar nicht überlegt. Ich habe nun deine Funktion genommen und beim runden einfach das so eingebaut wie in der Funktion von mermshaus damit ich weiterhin die Rundung mit 0.01 oder 1.00 angeben kann:
$x= round(($ergebnis / $runden) * $runden, 2);

Stimmt das so überhaupt? Die ergebnisse stimmen eigentlich. Die 2 gibt ja nur die genauigkeit des Runden an oder?

Ich erhalte auf jedenfall keinen Unterschied bei diesen beiden:
$x= round(($ergebnis / $runden) * $runden, 2);
und
$x= round($ergebnis / $runden) * $runden;

Kommt das aufs selbe an oder mache ich grad ein überlegungsfehler?

Gruss

Koda

 
SysOp
13-10-2015, 18:16 
 
Ich bin nicht sicher, ob mermshaus seine Funktion nicht nur als Ausgabefunktion verstanden wissen will. Dann finde ich seine Lösung elegant.

Der Unterschied zu meiner Funktion ist der, dass ich die Kursberechnung in der Funktion selbst habe, den neuen Wert berechne und das Ergebnis dann runde.
Ausserdem unterscheide ich zwischen dem errechneten Wert mit frei definierbarer Nachkommastelle (also zu.B. 5) mit dem man dann weiterrechnen könnte) und der formatierten Ausgabe mit definierbaren Nachkommastellen (im Bsp. 2).

Entscheidend wird sowas bei sehr kleinen Beträgen, die man weiter verwenden möchte.

Ein sehr übetriebenes Bsp:
1 Paket mit 1 Mio Stecknadeln kostst €2, was kostet ein Stk in Dollar und wie hoch ist die Ust? in Dollar.

Ich weiss ja nicht, was du mit dem Umrechner weiter machen möchtest :)

 
Koda
13-10-2015, 19:10 
 
Hi

Vielen Dank. Es geht um eine Plattform mit einer Grundwährung z.B. CHF oder auch EUR. Nun möchte ich die Plattform umstellen können auf z.B. USD.

Die Preise werden danach umgerechnet und angezeigt. Gerechnet wird damit dann in einer art Warenkorb berechnet. Dort sind dann die Preise aber bereits in einer Datenbank und müssen nicht mehr umgerechnet werden.

Deine Funktion gefällt mir da sehr gut. Nur sehe ich noch nicht wie ich am einfachsten angeben kann das der kleinste betrag 0.01, 0.05, 0.10 oder auch 1.00 ist. Daher meine Frage mit der round erweiterung ob das so passen könnte.

Gruss

Oliver

 
mermshaus
13-10-2015, 19:41 
 
Ich habe nur versucht, die Frage so zu beantworten, wie ich sie verstanden habe. Mir ging es da nicht um kaufmännische Aspekte. Dass auch die Isländer und Schweizer nicht jeden Kleinstbetrag auf volle Rappen oder Kronen runden, sollte ja klar sein. Das ist ja auch beim Euro nicht anders, wenn man sich zum Beispiel mal Benzinpreise an Tankstellen ansieht, die manchmal auch mit Zehntel-Cent angegeben werden (jedenfalls früher).

Fürs kaufmännische Rechnen dürfte es durchaus Sinn ergeben oder gar notwendig sein, weitere Nachkommastellen zu verwalten. Da bin ich aber kein Experte. Ich habe hier eine andere Fragestellung beantwortet.

 
SysOp
14-10-2015, 12:23 
 
Bin eigentlich auch nur durch die Währung und Umrechnungskurse auf die Idee gekommen, als reine Ausgabefunktion in einer Webseite würde ich durchaus mermshaus Lösung bevorzugen.

 
Koda
14-10-2015, 12:45 
 
Ich erhalte auf jedenfall keinen Unterschied bei diesen beiden:
$x= round(($ergebnis / $runden) * $runden, 2);und
$x= round($ergebnis / $runden) * $runden;Kommt das aufs selbe an oder mache ich grad ein überlegungsfehler?

Ok vielen dank euch zwei. Kann mir noch jemand den Unterschied der beiden schreibweisen nennen? Ich erhalte wie gesagt die selben ergebnisse.

Ich möchte nachher die Währung mit dem kleinsten wert angeben können wie oben geschrieben (0.01, 0.05, 1.00).

Gruss

Koda

 
SysOp
14-10-2015, 13:55 
 
gar nicht so einfach.

($ergebnis / $runden) * $runden macht eigentlich keinen Sinn. Etwas durch 7 zu teilen und danach mit 7 zu multiplizieren ist eigentlich nicht sehr sinnvoll.
Variante 1: das ,2 gibt an, auf wieviele Stellen gerundet werden soll.


Noch einmal, mermshaus Lösung behebt deine Anfangsfrage sicher besser als meine, ich wollte nur vorsichtshalber auf den Kaufmännischen Aspekt hinweisen.

 
Koda
14-10-2015, 14:24 
 
gar nicht so einfach.

($ergebnis / $runden) * $runden macht eigentlich keinen Sinn. Etwas durch 7 zu teilen und danach mit 7 zu multiplizieren ist eigentlich nicht sehr sinnvoll.
Variante 1: das ,2 gibt an, auf wieviele Stellen gerundet werden soll.


Noch einmal, mermshaus Lösung behebt deine Anfangsfrage sicher besser als meine, ich wollte nur vorsichtshalber auf den Kaufmännischen Aspekt hinweisen.

Vielen Dank. Das mit dem dividieren und dann multiplizieren gibt wenig sinn aber wenn du nach einer Lösung suchst auf 5 rappen zu runden findest du sehr oft genau diese funktion :confused:

Vielen Dank euch beiden

Gruss

Koda

 
mermshaus
14-10-2015, 17:11 
 
Das findest du vermutlich für Sprachen, die Integer-Division kennen. Integer-Division bedeutet: Wenn zwei Integer durcheinander geteilt werden und das Ergebnis keinen Integer ergibt, dann wird der Rest verworfen, sodass es wieder einen Integer ergibt.

10 / 5 → 2 (Rest 0) → 2
14 / 5 → 2 (Rest 4) → 2

Das gibt es aber in PHP nicht. PHP liefert in dem Fall eine Fließkommazahl (14 / 5 → 2.8).

Der Operator für Integer-Division heißt auch manchmal div oder \ (umgekehrtes Geteilt-Zeichen).

 
fireweasel
14-10-2015, 20:47 
 
Integer-Division bedeutet: Wenn zwei Integer durcheinander geteilt werden und das Ergebnis keinen Integer ergibt, dann wird der Rest verworfen, sodass es wieder einen Integer ergibt.

10 / 5 → 2 (Rest 0) → 2
14 / 5 → 2 (Rest 4) → 2

Das gibt es aber in PHP nicht. PHP liefert in dem Fall eine Fließkommazahl (14 / 5 → 2.8).

Mit ein bisschen Herumtrickserei funktioniert das auch in PHP. Seit PHP 5.6 kann man GMP-Zahlen auch mit mathematischen Operatoren verknüpfen (http://docs.php.net/manual/en/class.gmp.php):

$num = gmp_init(14);
$result = $a / 5 * 5;
var_dump((int) $result); // int(10)
var_dump((double) $result); // float(10)


Mit GMP könnte man prinzipiell auch Währungs-Werte berechnen. Da aber die PHP-Erweiterung (http://docs.php.net/manual/en/book.gmp.php) nur die Ganzzahl-Funktionen der LibGMP (https://gmplib.org/) anzapft, muss man sich dabei auf die kleinste darstellbare Währungseinheit beschränken und Ein- und Ausgaben entsprechend umrechnen (oder umformatieren).

 
mermshaus
15-10-2015, 14:44 
 
Mehr random Operator-Overloading, das nur auf C-Ebene definierbar ist, hat PHP natürlich gerade noch gefehlt. :D

Na ja, solange die Extension nicht…

$ php -m | grep gmp
$

…mitinstalliert wird, lohnt es sich leider allgemein eh nicht wirklich, sich damit zu befassen.

 
h3ll
15-10-2015, 15:23 
 
function int_div($dividend, $divisor) {
return ($dividend - $dividend % $divisor) / $divisor;
}

var_dump(int_div(10, 5)); // int(2)
var_dump(int_div(14, 5)); // int(2)

 
mermshaus
15-10-2015, 16:23 
 
Oder auch einfach (int)($a/$b), aber mit einer Funktion wäre es im Zweifel wohl semantisch eindeutiger.

 
fireweasel
18-10-2015, 00:40 
 
Ist gmp_div(14, 5); zu kompliziert?

Mehr random Operator-Overloading, das nur auf C-Ebene definierbar ist, hat PHP natürlich gerade noch gefehlt.
Ich habe nicht geschrieben, dass es mir gefällt, nur auf das Vorhandensein dieses Features hingewiesen. ;)

solange die Extension nicht (...) mitinstalliert wird (...)
Bei den Standard-(Windows)-Binaries von php.net ist sie dabei.

 
mermshaus
18-10-2015, 13:32 
 
Bei meinem Ubuntu 14.04 hier leider nicht. Ist natürlich trivial, die zu installieren, falls man Rechte dazu hat.

Ich finde es schwierig, die Frage zu beantworten, welche PHP-Extensions man sicher nutzen kann, weil sie quasi überall installiert sind.

Dass sich solche Probleme nicht stellen, wenn das Zielsystem bekannt oder administrierbar ist, ist klar.

Ein phpversions.info für Extensions würde mich mal sehr interessieren. Bzw. einfach Zahlen dazu.

 
h3ll
18-10-2015, 22:46 
 
PHP 7 hat übrigens intdiv().

 
fireweasel
19-10-2015, 00:56 
 
Bei meinem Ubuntu 14.04 hier leider nicht. Ist natürlich trivial, die zu installieren, falls man Rechte dazu hat.

Ich finde es schwierig, die Frage zu beantworten, welche PHP-Extensions man sicher nutzen kann, weil sie quasi überall installiert sind.

Laut PHP: Membership - Manual (http://docs.php.net/manual/en/extensions.membership.php) gehört GMP zu den "bundled Extensions" (aber mit Abhängigkeit von externen Libraries -- BCMath kommt da ohne aus).

PHP 7 hat übrigens intdiv().
Der Fortschritt ist nicht aufzuhalten! :cool:
Div (http://ruby-doc.org/core/Numeric.html#method-i-divmod)m (https://docs.python.org/2/library/functions.html#divmod)o (http://linux.die.net/man/3/div)d (https://msdn.microsoft.com/en-us/library/system.math.divrem.aspx)( (http://www.lispworks.com/documentation/HyperSpec/Body/f_floorc.htm)) (http://zvon.org/other/haskell/Outputprelude/divMod_f.html) bekommen wir dann sicher in PHP 9.
In PHP 11 liefert es dann sogar zwei Rückgabewerte statt eines numerisch indizierten Arrays.
Und in PHP 13 gibts schließlich eine Modulo-Funktion, die mathematisch korrekt rechnet (http://docs.php.net/manual/en/language.operators.arithmetic.php#V112730). :respekt:

 
mermshaus
19-10-2015, 12:42 
 
Laut PHP: Membership - Manual gehört GMP zu den "bundled Extensions" (aber mit Abhängigkeit von externen Libraries -- BCMath kommt da ohne aus).

Ich habe mittlerweile zwei Hosting-Anbieter gefunden, die gmp offenbar nicht haben (Strato und hostgator). Es ist leider extrem schmerzhaft, überhaupt Modul-Auflistungen zu finden. Mit so Details halten sich viele Anbieter nicht auf.

Das betrifft natürlich zahlreiche Module/Extensions und ist ärgerlich, weil man dadurch etliche eigentlich verfügbare Features nicht nutzen kann (gutes Beispiel ist sicher das intl-Zeug), wenn man Software/Libraries schreiben möchte, die möglichst überall laufen.

Ich habe wegen so was schon öfter bestimmte Features/Funktionsaufrufe aus Code rausgenommen.


Alle Zeitangaben in WEZ +2. Es ist jetzt 21:58 Uhr.