Anzahl der nachkommastellen bestimmen nur wie?

Status
Nicht offen für weitere Antworten.

florian1x

Aktives Mitglied
ich würde ja sagen, dass es da eine funktion in MATH gibt.
Weiß nur nicht welche.

Also vlt erstmal den Hintergrund ...

In der Schule schrieben wir eine Klasse zum Rechnen mit Brüchen.
Damit habe ich auch keine Probleme. Mein Problem ist nun, dass ich immer etwas mehr mache als ich machen müsste. Mir reicht es nicht nur einen Konstruktor mit zwei parametrn für nenner und zähler zu haben, ich will auch aus einer double zahl den Bruch erzeugen können. Dazu muss ich wissen, wieviele nachkommastellen die zahl hat.

bsp. 4,5 wära dann 45/10 bzw 9/2.

ich könnte natürlich auch über eine schleife machen

Code:
    int k=0;
    while( (zahl-(int)zahl)>0 ){
        k++;
        zahl=zahl*10;
    }

müsste eigentlich funktionieren. hab ich jetzt nicht getestet, aber sollte tun.

nur da muss es doch nen schönen funktionsaufruf für geben




zudem hab ich noch eine Frage
ist die Programmiereung so in Ordnung?
Code:
public class Bruch {
	//globale Variablen
	private int zaehler;
	private int nenner;
	
	//Konstruktoren
	public Bruch(){ 
		zaehler = 0;
		nenner  = 0;
	} 
	public Bruch(int z,int n){ 
		zaehler = z;
		nenner  = n;
	}
	public Bruch(double d){ 
		int k = (int)Math.log10(Math.abs(d));
	}
	
	//Methoden (Grudrechenarten)
	/**
	 * Die Methode [i]Prod(Bruch b1,Bruch b2)[/i] multipliziert zwei Brüche.
	 * @param b1 stellt den ersten Bruch dar.
	 * @param b2 stellt den zweiten Bruch dar.
	 */
	public void Prod(Bruch b1,Bruch b2){
		zaehler = b1.zaehler*b2.zaehler;
		nenner  = b1.nenner*b2.nenner;
		this.reduce();
		this.checkNenner();
	}
	/**
	 * Die Methode [i]Prod(Bruch b1,int value)[/i] multipliziert einen Bruch mit einem Integer.
	 * @param b1 stellt den ersten Bruch dar.
	 * @param value stellt den Integer dar.
	 */
	public void Prod(Bruch b1,int value){
		zaehler = b1.zaehler*value;
		nenner  = b1.nenner;
		this.reduce();
		this.checkNenner();
	}
	/**
	 * Die Methode [i]Quot(Bruch b1,Bruch b2)[/i] dividiert zwei Brüche.
	 * @param b1 stellt den ersten Bruch dar.
	 * @param b2 stellt den zweiten Bruch dar.
	 */
	public void Quot(Bruch b1,Bruch b2){
		zaehler = b1.zaehler*b2.nenner;
		nenner  = b1.nenner*b2.zaehler;
		this.reduce();
		this.checkNenner();
	}
	/**
	 * Die Methode [i]Quot(Bruch b,int value)[/i] dividiert einen Bruch durch einen Integer.
	 * @param b stellt den ersten Bruch dar.
	 * @param value stellt den Integer dar.
	 */
	
	public void Quot(Bruch b,int value){
		zaehler = b.zaehler;
		nenner  = b.nenner*value;
		this.reduce();
		this.checkNenner();
	}
	/**
	 * Die Methode [i]Quot(int value,Bruch b)[/i] dividiert einen Integer durch einen Bruch.
	 * @param b stellt den ersten Bruch dar.
	 * @param value stellt den Integer dar.
	 */
	public void Quot(int value,Bruch b){
		zaehler = value*b.nenner;
		nenner  = b.zaehler;
		this.reduce();
		this.checkNenner();
	}
	/**
	 * Die Methode [i]Summe(Bruch b1,Bruch b2)[/i] addiert zwei Brüche.
	 * @param b1 stellt den ersten Bruch dar.
	 * @param b2 stellt den zweiten Bruch dar.
	 */
	public void Summe(Bruch b1,Bruch b2){
		if(b1.nenner!=b2.nenner){
			b1.Skal(b2.nenner);
			b2.Skal(b1.nenner);
		}
		zaehler = b1.zaehler+b2.zaehler;
		nenner = b1.nenner;
		this.reduce();
		this.checkNenner();
	}
	/**
	 * Die Methode [i]Summe(Bruch b,int value)[/i] addiert einen Bruch mit einen Integer.
	 * @param b stellt den ersten Bruch dar.
	 * @param value stellt den Integer dar.
	 */
	public void Summe(Bruch b,int value){
		zaehler = b.zaehler+value*b.nenner;
		nenner = b.nenner;
		this.reduce();
		this.checkNenner();
	}
	/**
	 * Die Methode [i]Diff(Bruch b1,Bruch b2)[/i] subrahiert zwei Brüche.
	 * @param b1 stellt den ersten Bruch dar.
	 * @param b2 stellt den zweiten Bruch dar.
	 */
	public void Diff(Bruch b1,Bruch b2){
		if(b1.nenner!=b2.nenner){
			b1.Skal(b2.nenner);
			b2.Skal(b1.nenner);
		}
		zaehler = b1.zaehler-b2.zaehler;
		nenner = b1.nenner;
		this.reduce();
		this.checkNenner();
	}
	/**
	 * Die Methode [i]Diff(Bruch b,int value)[/i] subtrahiert einen Bruch mit einen Integer.
	 * @param b stellt den ersten Bruch dar.
	 * @param value stellt den Integer dar.
	 */
	public void Diff(Bruch b,int value){
		zaehler = b.zaehler-value*b.nenner;
		nenner = b.nenner;
		this.reduce();
		this.checkNenner();
	}
	/**
	 * Die Methode [i]Diff(Bruch b,int value)[/i] subtrahiert einen Integer mit einen Bruch.
	 * @param value stellt den Integer dar.
	 * @param b stellt den ersten Bruch dar.
	 */
	public void Diff(int value,Bruch b){
		zaehler = value*b.nenner-b.zaehler;
		nenner = b.nenner;
		this.reduce();
		this.checkNenner();
	}
    //Methoden (Zusatzfunktionen)
	/**
	 * Die Methode [i]Skal(int value)[/i] erweiterten einen Bruch um einen Integer-Wert.
	 * @param value stellt den Integer dar.
	 */
	public void Skal(int value){
		zaehler = value*zaehler;
		nenner  = value*nenner;
		this.checkNenner();
	}
	/**
	 * Die Methode [i]Betrag(Bruch b)[/i] gibt den Dezimalwert eines Bruches zurück.
	 * @param b stellt den Bruch dar.
	 * @return gibt den Dezimalwert des Bruches zurück.
	 */
	public static double Betrag(Bruch b){
		return b.zaehler/b.nenner;
	}
	/**
	 * Die Methode [i]ggt(int value1, int value2)[/i] gibt den größten gemeinsamen Teiler

	 * von zwei Integer-Werten zurück.
	 * @param value1 ist der erste Integer.
	 * @param value2 ist der zweite Integer.
	 * @return der ggt.
	 */
	public static int ggt(int value1, int value2)
	{
	    while(value1!=value2)
	    {
	        if(value1>value2)
	        	value1=value1-value2;
	        else
	        	value2=value2-value1;
	    }
	    return value1;
	}
	/**
	 * Die Methode [i]reduce()[/i] dient zum kürzen eines Bruches.
	 */
	public void reduce()
	{
	    int g=ggt(zaehler,nenner);
	    zaehler=zaehler/g;
	    nenner=nenner/g;
	}
	/**
	 * Die Methode [i]checkNenner()[/i] überprüft ob der Nenner negativ ist.

	 * Sollte dies der Fall sein, werden die Vorzeichen von Zähler und Nenner invertiert.
	 */
	public void checkNenner(){
		if(nenner<0){
			zaehler=-zaehler;
			nenner=-nenner;
		}
	}
	//getMethoden
	/**
	 * Die Methode [i]getZaehler()[/i] gibt den Zahler des Bruches zurück.
	 */
	public int getZaehler(){
		return this.zaehler;
	}
	/**
	 * Die Methode [i]getNenner()[/i] gibt den Nenner des Bruches zurück.
	 */
	public int getNenner(){
		return this.nenner;
	}
	
    //setMethoden
	/**
	 * Die Methode [i]setZaehler(int value)[/i] setzt den Zaehler auf dem übergeben

	 * Integer-Wert.
	 * @param value stellt den Wert dar, auf den der Zaehler gesetzt werden soll.
	 */
	public void setZaehler(int value){
		zaehler=value;
	}
	/**
	 * Die Methode [i]setNenner(int value)[/i] setzt den Nenner auf dem übergeben

	 * Integer-Wert.
	 * @param value stellt den Wert dar, auf den der Nenner gesetzt werden soll.
	 */
	public void setNenner(int value){
		nenner=value;
	}
	/**
	 * Die Methode [i]setBruch(int z,int n)[/i] setzt den Bruch auf die Übergebenen Werte.
	 * @param z steht für den Zaehler
	 * @param n steht für den Nenner
	 */
	public void setBruch(int z,int n){
		zaehler=z;
		nenner=n;
	}
}
 
Erstmal: Mein Problem ist nun, dass ich immer etwas mehr mache als ich machen müsste.
Nicht wirklich ein Problem. Eher gut.

ist die Programmiereung so in Ordnung?
Ja. (Abgesehen davon, dass man Methodennamen eigentlich klein schreibt). Habe zwar nicht überprüft, ob jede einzlene Funktion das richtige tut, aber beim ersten drüberschauen gibt's (imho) nichts zu beanstanden.

Zur eigentlichen Frage:

double d = 1.0 / 3.0;
Bruch b = new Bruch(d);

Welchen Wert sollten Zähler und Nenner jetzt haben? Eigentlich Klar: 1 und 3, natürlich. Das dumme ist nur, dass 1.0/3.0 unendlich viele Nachkommastellen hat, und in einem double garnicht richtig dargestellt werden kann. Die Idee mit der Schleife sieht erstmal OK aus, nur kann es passieren dass die Schleife NIE endet - z.B. wenn man 1.0/3.0 übergibt...

Die Ursache dafür liegt letztendlich bei der Art der Darstellung von Gleitkommazahlen im Computer (Websuche nach "IEEE 754".) Du kannst dir z.B. mal ansehen, wie die Zahl 0.333333.... im Computer dargestellt wird:
http://babbage.cs.qc.edu/IEEE-754/Decimal.html

Dazu kommt noch, dass eine Double-Zahl ja z.B. auch
1.234e-100
sein kann - das ist eine Zahl mit ca. 100 Nachkommastellen, die man NICHT mehr als Bruch (in dieser Form) darstellen kann.

Eine "einfache" Lösung für dein Problem wüßte ich jetzt aber nicht. Etwas ähnliches(!), wie das, was du vorhast, wird gemacht, wenn man eine double-Zahl als String ausgibt. Die Umwandlung wird in diesem Fall von einer (nicht-öffentlichen) Klasse "java.lang.FloatingDecimal" erledigt. Einige "Zitate" aus dieser Klasse:
Code:
decExp = (int)Math.floor((d2-1.5D)*0.289529654D + 0.176091259 + (double)binExp * 0.301029995663981 );
...
        /*
	 * HACK!! For exact powers of two, the next smallest number
	 * is only half as far away as we think (because the meaning of
	 * ULP changes at power-of-two bounds) for this reason, we
	 * hack M2. Hope this works.
	 */
...
	/*
	 * Construct, Scale, iterate.
	 * Some day, we'll write a stopping test that takes
	 * account of the assymetry of the spacing of floating-point
	 * numbers below perfect powers of 2
	 * 26 Sept 96 is not that day.
	 * So we use a symmetric test.
	 */
...
        Bbits = nFractBits + B2 + (( B5 < n5bits.length )? n5bits[B5] : ( B5*3 ));
	tenSbits = S2+1 + (( (S5+1) < n5bits.length )? n5bits[(S5+1)] : ( (S5+1)*3 ));
	if ( Bbits < 64 && tenSbits < 64){
	    if ( Bbits < 32 && tenSbits < 32){
		// wa-hoo! They're all ints!
                ...
(Vielleicht sollte man erwähnen, dass in dieser Klasse immerhin 14 (vierzehn!) mal das Wort "Hack" vorkommt, und an einiges Stellen die Kommentare ziemlich deutlich zeigen, dass diejenigen, die das geschreiben haben, selbst nicht so recht wußten, was sie da tun)

Der Hauptteil dieser Uwandlungen wird in einer Methode gemacht, vor deren Aufruf immer der Kommentar steht
Code:
	// call the routine that actually does all the hard work.

Genauso könntest du es auch machen. Das einzige, was mir überhaupt einfallen würde, wäre demnach ein (weiterer) "Hack": Man erstellt sich einen String aus der Kommazahl, und holt sich dort die Informationen raus, die man braucht:
- Falls das erste Zeichen ein "-" ist, ist die zahl negativ
- Der folgende Teil bis zum "." ist der Vorkomma-Anteil
- Der Teil zwischen dem "." und dem Ende (oder einem "e" oder "E") ist der Nachkomma-Anteil
- Der Teil nach dem "e" oder "E" ist der Exponent (zur Basis 10, erfeulicherweise)
Daraus sollte man sich Zähler und Nenner basteln können.

Vielleicht hilft das ja.
 
danke
klingt doch gut ich werde mich gleich mal dran setzten.

auch wenn ich nicht genau weiß wie das mit dem double zu string funktioniert.

Könntest du mir denn sagen, wie ich herrausfinde an welcher stelle das Komma doer so sitzt?

hab ich bisher nie gemacht. Ich schau schonmal ob ichs selber herraus finde.
 
Code:
String s = String.valueOf(meinDouble);

if (s.charAt(0) == '-') -> negativ

int dotIndex = s.indexOf(".");

int eIndex = s.indexOf("e");
if (eIndex == -1) eIndex = s.indexOf("E"); // Kann danach immernoch -1 sein
Damit kann man rausfinden, an welcher Stelle der "." und das "e" oder "E" stehen. Den eigentlichen String kann man dann mit
String sub = s.substring(startIndex, endIndex);
zerpflücken (die substrings am besten immer erstmal ausgeben, um zu sehen, ob man sich bei den Indizes nicht um +/- 1 vertan hat).

Die Substrings sind dann Strings, die nur ganzzahlige Werte enhalten, nämlich
String vorKommaString = s.substring(...);
String nachKommaString = ...
String exponentString = ...

Die int-Werte dazu erhält man mit

int vorKomma = Integer.parseInt(vorKommaString);

Um rauszufinden, wieviele Stellen eine ganze Zahl hat (insbesondere der Nachkommaanteil) kannst du

int stellen = (int)Math.floor((Math.log(nachKomma) / Math.log(10))) + 1;

rechnen.

Damit sollte es hinzukriegen sein 😀
 
Code:
if (s.charAt(0) == '-') -> negativ

da sacht der mir nen Fehler. Es wird ein statement erwartet.

ich weiß auch niocht was der Pfeil bedeutet.
 
der pfeil ist keine java syntax sondern soll nur bedeiten, wenn die bedingung der if abfrage gilt ist die zahl negativ
 
Irgendwie will das alles nicht

Code:
      public void setBruch(double d){
		String s = String.valueOf(d); 
		int vorKomma = Integer.parseInt(s.substring(0,s.indexOf("."))); 
		int nachKomma = Integer.parseInt(s.substring(s.indexOf(".")+1,s.indexOf("E")));
		int exponent = Integer.parseInt(s.substring(s.indexOf("E")+1));
		
		//Ausgaben zum Test
		System.out.println(d);
		System.out.println(vorKomma);
		System.out.println(nachKomma);
		System.out.println(exponent);
	}
	public static void main(String[] args) {
		Bruch b = new Bruch();
		b.setBruch(Math.pow(10, 5)/3.0);//wert zum testen
	}

da kommt jedoch folgender fehler

Exception in thread "main" java.lang.StringIndexOutOfBoundsException: String index out of range: -7
in der zeile:
nachKommaString = Integer.parseInt(s.substring(s.indexOf(".")+1,s.indexOf("E")));
 
Mehr Ausgaben manchen, und mehr Zwischenergebnisse speichern!

Ein einfaches
String s = String.valueOf(d);
System.out.println(d);
hätte schon gezeigt, dass in diesem String garkein "E" vorkommt. Und dann ist s.indexOf("E") == -1. (Hatte ich aber schon ziemlich deutlich gesagt!)

Das hätte auch den Vorteil, dass du
s.indexOf("E")
und
s.indexOf(".")
nicht mehrmals berechnen müßest.

BTW: Wenn du ein bißchen mit der Klasse DecimalFormat rumspielst,
http://java.sun.com/j2se/1.4.2/docs/api/java/text/DecimalFormat.html (Siehe "Scientific Notation")
kannst du erreichen, dass immer die normierte wissenschaftliche Schreibweise (mit dem "E") verwendet wird. Wenn du Java 1.5 verwendest geht das aber auch noch deutlich einfacher:
String s = java.util.Formatter.format("%e", doubleWert);

BTW2: Bei double-Werten werden u.U. ziemlich viele Stellen ausgegeben. Du solltest aufpassen, dass die Strings nicht zu lang werden, die du in Integer.parseInt übergibst. Besser wäre in diesem Fall dann wohl auch Long.parseLong ...
Code:
public class BruchTest
{
    public static void setBruch(double d)
    {
      String s = String.valueOf(d);

      System.out.println("Zahl "+d);

      int dotIndex = s.indexOf(".");
      String vorKommaString = s.substring(0,dotIndex);
      long vorKomma = Long.parseLong(vorKommaString);

      long nachKomma = 0;
      long exponent = 1;
      int nachKommaStellen = 0;
      int eIndex = s.indexOf("E");
      if (eIndex == -1) // Kein Exponent gefunden
      {
          String nachKommaString = s.substring(dotIndex+1,s.length()); // Bis zum ende
          nachKommaStellen = nachKommaString.length();
          nachKomma = Long.parseLong(nachKommaString);
      }
      else
      {
          String nachKommaString = s.substring(dotIndex+1,eIndex); // Bis zum "E"
          nachKommaStellen = nachKommaString.length();
          nachKomma = Long.parseLong(nachKommaString);

          String exponentString = s.substring(eIndex+1);
          exponent = Long.parseLong(exponentString);
      }

      //Ausgaben zum Test
      System.out.println("Vor Komma "+vorKomma);
      System.out.println("Nach komma "+nachKomma);
      System.out.println("Nachkommastellen "+nachKommaStellen);
      System.out.println("Exponent "+exponent);
   }
   public static void main(String[] args) {
      setBruch(Math.pow(10, 5)/3.0);//wert zum testen
      setBruch(Math.PI);
      setBruch(Math.E / Math.pow(2, 20));
      setBruch(1L<<50L);

      // Vorsicht bei solchen Zahlen: Als Nachkommaanteil gibt er hier 10 aus!!!
      setBruch(0.0010);
   }
}
 
Wenn du keine Lus mehr auf Handarbeit hast noch ein Tipp versuch es mal mit der Klasse BigDecimal.
Da ist eigentlich alles was du brauchst schon implementiert und reichlich durchdacht.
 
Status
Nicht offen für weitere Antworten.

Zurück
Oben