Suche Lib für "schöne Zahlen"

Hallo,

ich möchte Folgendes erreichen:

System.out.println(12.3456 * 434.8);
// 1. Schritt: auf 5367.86688 kürzen
// 2. Schritt: auf 5367.87 kürzen
// 3. Schritt: auf 5367.8 kürzen
// => soll auf 5367.8 gekürzt werden (1 Nachkommastelle)
System.out.println(12.3456 * 9.99);
// 1. Schritt: 123.332544
// 2. Schritt: 123.333
// 3. Schritt: 123.33
// => soll auf 123.33 gekürzt werden (2 Nachkommastellen)
System.out.println(123.46341140939597 * 9.99);
// 1. Schritt: 1233.3994799798659
// 2. Schritt: 1233.399
// 3. Schritt: 1233.4
// => soll auf 1233.40 gekürzt werden (2 Nachkommastellen)

Es sollen immer zwei Zahlen multipliziert werden, und auf die Anzahl Nachkommastellen gekürzt werden, die der zweite Faktor hat. Wenn allerdings nach dem zweiten Schritt die letzte und vorletzte Ziffer 9 ist, dann soll aufgerundet werden, sonst nicht.

Gibt es dafür eine Lib?

Also: 1. Schritt double Multiplikation, 2. Schritt runden auf (x+1) Nachkommastellen, 3. Schritt abrunden auf x Nachkommastellen, wobei nur bei xxx99 aufgerundet werden soll (Sonderfall, wahrscheinlich Rechenungenauigkeit)…

Eine „fertige Lib“ gibt es für genau das sicher nicht. Es gibt verschiedene mögliche Anforderungen die mit „Runden“ und „Nachkommastellen“ zu tun haben, und die immer wieder auftauchen. In unserem Wiki gibt es ein paar Informationen dazu: https://wiki.byte-welt.net/wiki/Gleitkommazahlen_mit_Java_runden .

Aber bei der konkreten Beschreibung ergeben sich einige Fragen:

  1. Was soll das mit den „Schritten“? Wenn immer nur einzelne Stellen abgeschnitten werden sollen, scheint das überflüssig zu sein (es wird ja anscheinend immer _ab_gerundet. Wenn „richtig“ gerundet werden würde, könnte es Konstellationen geben, wo das eine Rolle spielt)
  2. Es gibt keinen Unterschied zwischen 1233.40 und 1233.4.
  3. Eine Zahl wie „0.1“ kann im Computer normalerweise nicht dargestellt werden. Es wird vielleicht „0.1“ angezeigt, wenn man sie ausgibt. In Wirklichkeit ist die Zahl aber „0.1000000000000000055511151231257…“
  4. Wo kommen die Eingabezahlen her, und sind die irgendwie eingeschränkt? Was soll bei `12.3456 * 0.000000001" rauskommen?
  5. Was soll mit dem Ergebnis gemacht werden?

So, wie es bisher klingt, klingt es, als ginge es nur darum, eine „schöne“ Ausgabe zu bekommen. Die Frage ist aber, inwieweit das z.B. „zerlegt“ werden könnte. Pseudocode:

double faktorA = 12.3456;
double faktorB = 9.99;
int anzahlNachkommastellen = bestimmeDieFür(faktorB);
double produkt = faktorA * faktorB;
String ergebnis = formatiere(produkt, anzahlNachkommastellen);
1 „Gefällt mir“

Danke, genau so wollte ich auch vorgehen…

Die Fragen sind zunächst(!) einmal, wie ich die Anzahl der Nachkommastellen einer double-Zahl bestimmen kann (also diese, die man sieht, wenn man die Zahl printed)… und wie ich dann immer auf die bestimmte Anzahl an Nachkommastellen abrunden kann - aber mit der ~ „…99-Ausnahme“…

Ich bedanke mich, dass du mir hilfst.

Nochmal kürzer: Man kann da ein paar Zeilen code schreiben, die „Die Lösung“ sind. Aber das ist fast immer irgendwelches String-Gefrickel. Und wenn jemand meint, „Die Lösung“ gefunden zu haben, kann ich praktisch garantiert Eingaben liefern, bei denen „Die Lösung“ eben nicht funktioniert.

Das mit dem „Aufrunden bei …99“ klingt nach Murks. Was auch immer der Zusammenhang ist, wo das verwendet werden soll: *Jeder, der das beobachtet, wird über das unerklärliche Verhalten irritiert sein. (Und sowas auf der Ebene von Unit-Tests abzudecken ist sowieso illusorisch…)

Aber … wie auch immer, hier ist code…

import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.Locale;

public class PrintNiceNumbers
{
    public static void main(String[] args)
    {
        System.out.println(createString(12.3456, 434.8));
        System.out.println(createString(12.3456, 9.99));
        System.out.println(createString(123.46341140939597, 9.99));
    }
    
    private static String createString(double factorA, double factorB) 
    {
        int n = numberOfFractionalDigits(factorB);
        double product = factorA * factorB;
        double candidate = truncate(product, n + 1);
        if (String.valueOf(candidate).endsWith("99")) 
        {
            double value = round(product, n);
            return String.valueOf(value);
        }
        double value = truncate(product, n);
        return String.valueOf(value);
    }
    
    private static double truncate(double value, int decimalPoints)
    {
        double d = Math.pow(10, decimalPoints);
        return (int) (value * d) / d;
    }

    private static double round(double value, int decimalPoints)
    {
        double d = Math.pow(10, decimalPoints);
        return Math.rint(value * d) / d;
    }

    private static int numberOfFractionalDigits(double value)
    {
        DecimalFormat decimalFormat = new DecimalFormat("0.#",
            DecimalFormatSymbols.getInstance(Locale.ENGLISH));
        decimalFormat.setMaximumFractionDigits(340);
        String s = decimalFormat.format(value);
        int dotIndex = s.indexOf(".");
        if (dotIndex < 0)
        {
            return 0;
        }
        return s.substring(dotIndex + 1).length();
    }
}
1 „Gefällt mir“

Weshalb muss das English sein? Warum nicht ROOT oder German?

Mich verwirrt das, und mein Chef würde magische Konstanten nicht akzeptieren…

Bei ENGLISH kann man sicher sein, dass man den . an der richtigen Stelle findet. Bei GERMAN würde eine Zahl wie 1.234 eben als 1,234 formatiert werden (mit Komma statt Punkt…)

1 „Gefällt mir“

Nun ja, (das werden sie nicht tun, es wäre sehr abwegig) aber was ist, wenn die Engländer plötzlich beschließen würden, dass nur noch das Deutsche Zahlenformat für Dezimalzahlen gelten soll? Da wäre mir ROOT schon „sicherer“.

Keine Grenzen sind auf ewig festgelegt, gleiches gilt auch für die Sprache und die Zahlen. Deshalb hat „Geografie“ auch nicht mit Ländergrenzen zu tun. :wink: ( :older_man: Um mal einen Bogen zu aktuellen Geschehnissen zu schlagen, heute bedeutet das, wer glaubt, die russischen Grenzen seien auf ewig festgelegt, der hat im Geschichtsunterricht nicht aufgepasst. … Aber alles gut, wir müssen nur genug moderne Kampfpanzer liefern, dann wird niemandem Leid zugefügt.)

Dann nimm’ ROOT. Oder überleg’ dir, warum das unbedeutend ist :man_shrugging:

Also… ich möchte das einmal skizzieren:

import com.binance.api.client.BinanceApiRestClient;

public class MySymbol {
    private String symbol;
    private int stepSize;
    private int stepCount;
    private int precision;
    private double precisionDouble;

    public MySymbol(String symbol, int stepSize, int stepCount) {
        this.symbol = symbol;
        this.stepSize = stepSize;
        this.stepCount = stepCount;
        init();
    }

    private void init() {
        MyConnection.getInstance().testClient();

        precision = 3;
        precisionDouble = Math.pow(10, precision);
    }

    public void printSimulateList() {
        BinanceApiRestClient client = MyConnection.getInstance().getClient();
        double last = Double.parseDouble(client.get24HrPriceStatistics(symbol).getLastPrice());
        double step = stepSize / 100.0;
        for (int i = 0; i <= stepCount; i++) {
            double d1 = step * ((stepCount / 2) - (stepCount - i)) + 100.0;
            double d2 = last * d1;
            double d3 = round_u(d2) * 12.3 - round_d(d2) * 12.3;
            System.out.printf("%d\t%f\t%f\t%f\t%f%n", i, d1, d2, round_d(d2), round_u(d2));
            System.out.printf("Buy limit price: %f Sell limit price: %f Remaining: %f%n", round_d(d2), round_u(d2), d3);
        }
    }

    private double round_d(double d) {
        return Math.floor(d * precisionDouble) / precisionDouble;
    }

    private double round_u(double d) {
        return Math.ceil(d * precisionDouble) / precisionDouble;
    }
}

Ausgabe:

0	99,700000	6,851085	6,851000	6,852000
Buy limit price: 6,851000 Sell limit price: 6,852000 Remaining: 0,012300
1	99,730000	6,853146	6,853000	6,854000
Buy limit price: 6,853000 Sell limit price: 6,854000 Remaining: 0,012300
2	99,760000	6,855208	6,855000	6,856000
Buy limit price: 6,855000 Sell limit price: 6,856000 Remaining: 0,012300
3	99,790000	6,857269	6,857000	6,858000
Buy limit price: 6,857000 Sell limit price: 6,858000 Remaining: 0,012300
4	99,820000	6,859331	6,859000	6,860000
Buy limit price: 6,859000 Sell limit price: 6,860000 Remaining: 0,012300
5	99,850000	6,861392	6,861000	6,862000
Buy limit price: 6,861000 Sell limit price: 6,862000 Remaining: 0,012300
6	99,880000	6,863454	6,863000	6,864000
Buy limit price: 6,863000 Sell limit price: 6,864000 Remaining: 0,012300
7	99,910000	6,865515	6,865000	6,866000
Buy limit price: 6,865000 Sell limit price: 6,866000 Remaining: 0,012300
8	99,940000	6,867577	6,867000	6,868000
Buy limit price: 6,867000 Sell limit price: 6,868000 Remaining: 0,012300
9	99,970000	6,869638	6,869000	6,870000
Buy limit price: 6,869000 Sell limit price: 6,870000 Remaining: 0,012300
10	100,000000	6,871700	6,871000	6,872000
Buy limit price: 6,871000 Sell limit price: 6,872000 Remaining: 0,012300
11	100,030000	6,873762	6,873000	6,874000
Buy limit price: 6,873000 Sell limit price: 6,874000 Remaining: 0,012300
12	100,060000	6,875823	6,875000	6,876000
Buy limit price: 6,875000 Sell limit price: 6,876000 Remaining: 0,012300
13	100,090000	6,877885	6,877000	6,878000
Buy limit price: 6,877000 Sell limit price: 6,878000 Remaining: 0,012300
14	100,120000	6,879946	6,879000	6,880000
Buy limit price: 6,879000 Sell limit price: 6,880000 Remaining: 0,012300
15	100,150000	6,882008	6,882000	6,883000
Buy limit price: 6,882000 Sell limit price: 6,883000 Remaining: 0,012300
16	100,180000	6,884069	6,884000	6,885000
Buy limit price: 6,884000 Sell limit price: 6,885000 Remaining: 0,012300
17	100,210000	6,886131	6,886000	6,887000
Buy limit price: 6,886000 Sell limit price: 6,887000 Remaining: 0,012300
18	100,240000	6,888192	6,888000	6,889000
Buy limit price: 6,888000 Sell limit price: 6,889000 Remaining: 0,012300
19	100,270000	6,890254	6,890000	6,891000
Buy limit price: 6,890000 Sell limit price: 6,891000 Remaining: 0,012300
20	100,300000	6,892315	6,892000	6,893000
Buy limit price: 6,892000 Sell limit price: 6,893000 Remaining: 0,012300

Anforderungen:

  • Es darf nicht mehr verkauft als gekauft werden (die Differenz darf nicht negativ sein)
  • Die Börse akzeptiert aber nur Limit-Preise, die eine bestimmte Anzahl an Nachkommastellen nicht überschreiten
  • Der Restbetrag sollte genau 0 sein

Problem:

  • Es verbleibt immer ein Rest, > 0 :frowning:

Hast du eventuell e Idee?