it-swarm.com.de

Rundung auf eine beliebige Anzahl von signifikanten Stellen

Wie können Sie beliebige Zahl (nicht nur ganze Zahlen> 0) auf N signifikante Ziffern runden?

Wenn ich zum Beispiel auf drei signifikante Stellen runden möchte, suche ich nach einer Formel, die Folgendes annehmen könnte:

1 239 451 und Rückgabe 1 240 000

12.1257 und zurück 12.1

.0681 und zurück .0681

5 und zurück 5

Natürlich sollte der Algorithmus nicht hart codiert sein, um nur N von 3 zu handhaben, obwohl dies ein Anfang wäre.

78
DougN

Hier ist derselbe Code in Java ohne den 12.100000000000001-Fehler, den andere Antworten haben

Ich habe auch wiederholten Code entfernt, power in einen Typ Integer geändert, um Floating-Probleme zu verhindern, wenn n - d ausgeführt wurde, und das lange Intermediat klarer gemacht

Der Fehler wurde durch Multiplikation einer großen Zahl mit einer kleinen Zahl verursacht. Stattdessen teile ich zwei Zahlen von ähnlicher Größe.

EDIT
Weitere Fehler behoben. Check für 0 hinzugefügt, da dies zu NaN führen würde. Funktion mit negativen Zahlen funktionieren (Der ursprüngliche Code behandelt negative Zahlen nicht, weil ein Protokoll einer negativen Zahl eine komplexe Zahl ist).

public static double roundToSignificantFigures(double num, int n) {
    if(num == 0) {
        return 0;
    }

    final double d = Math.ceil(Math.log10(num < 0 ? -num: num));
    final int power = n - (int) d;

    final double magnitude = Math.pow(10, power);
    final long shifted = Math.round(num*magnitude);
    return shifted/magnitude;
}
100
Pyrolistical

ZUSAMMENFASSUNG:

double roundit(double num, double N)
{
    double d = log10(num);
    double power;
    if (num > 0)
    {
        d = ceil(d);
        power = -(d-N);
    }
    else
    {
        d = floor(d); 
        power = -(d-N);
    }

    return (int)(num * pow(10.0, power) + 0.5) * pow(10.0, -power);
}

Sie müssen also die Dezimalstelle der ersten Nicht-Null-Ziffer ermitteln, die nächsten N-1-Ziffern speichern und dann die N-te Stelle auf Basis der restlichen Zahlen runden.

Wir können log verwenden, um das erste zu tun.

log 1239451 = 6.09
log 12.1257 = 1.08
log 0.0681  = -1.16

Nehmen Sie für Zahlen> 0 die Decke des Protokolls. Bei Zahlen <0 nehmen Sie den Boden des Protokolls ein.

Jetzt haben wir die Ziffer d: 7 im ersten Fall, 2 im 2., -2 im 3..

Wir müssen die (d-N)th Ziffer runden. So etwas wie:

double roundedrest = num * pow(10, -(d-N));

pow(1239451, -4) = 123.9451
pow(12.1257, 1)  = 121.257
pow(0.0681, 4)   = 681

Dann machen Sie die Standard-Rundungssache:

roundedrest = (int)(roundedrest + 0.5);

Und mach den Pow los.

roundednum = pow(roundedrest, -(power))

Wo Leistung die oben berechnete Leistung ist.


Über die Genauigkeit: Die Antwort von Pyrolistical liegt tatsächlich näher am tatsächlichen Ergebnis. Beachten Sie jedoch, dass Sie 12.1 auf keinen Fall exakt darstellen können. Wenn Sie die Antworten wie folgt ausdrucken:

System.out.println(new BigDecimal(n));

Die Antworten sind:

Pyro's: 12.0999999999999996447286321199499070644378662109375
Mine: 12.10000000000000142108547152020037174224853515625
Printing 12.1 directly: 12.0999999999999996447286321199499070644378662109375

Verwenden Sie also die Antwort von Pyro!

15
Claudiu

Hier ist eine kurze und süße JavaScript-Implementierung:

function sigFigs(n, sig) {
    var mult = Math.pow(10, sig - Math.floor(Math.log(n) / Math.LN10) - 1);
    return Math.round(n * mult) / mult;
}

alert(sigFigs(1234567, 3)); // Gives 1230000
alert(sigFigs(0.06805, 3)); // Gives 0.0681
alert(sigFigs(5, 3)); // Gives 5
15
Ates Goral

Ist nicht die "kurze und süße" JavaScript-Implementierung

Number(n).toPrecision(sig)

z.B.

alert(Number(12345).toPrecision(3)

?

Tut mir leid, ich bin hier nicht verrückt, es ist nur so, dass die Verwendung der "roundit" -Funktion von Claudiu und der .toPrecision-Funktion in JavaScript andere Ergebnisse liefert, jedoch nur in der Rundung der letzten Ziffer.

JavaScript:

Number(8.14301).toPrecision(4) == 8.143

.NETZ

roundit(8.14301,4) == 8.144
10
Justin Wignall

Die (sehr schöne!) Lösung von Pyrolistical hat immer noch ein Problem. Der maximale Doppelwert in Java liegt in der Größenordnung von 10 ^ 308, während der minimale Wert in der Größenordnung von 10 ^ -324 liegt. Daher kann es zu Problemen kommen, wenn die Funktion roundToSignificantFigures auf etwas angewendet wird, das nur wenige Zehnerpotenzen von Double.MIN_VALUE besitzt. Zum Beispiel, wenn Sie anrufen

roundToSignificantFigures(1.234E-310, 3);

dann hat die Variable power den Wert 3 - (-309) = 312. Folglich wird die Variable magnitudeInfinity, und von da an ist alles Müll. Glücklicherweise ist dies kein unüberwindbares Problem: Nur der Faktor magnitude läuft über. Was wirklich zählt, ist das Produkt num * magnitude, und das läuft nicht über. Eine Möglichkeit zur Lösung dieses Problems besteht darin, die Multiplikation mit dem Faktor magintude in zwei Schritte aufzuteilen:


 public static double roundToNumberOfSignificantDigits(double num, int n) {

    final double maxPowerOfTen = Math.floor(Math.log10(Double.MAX_VALUE));

    if(num == 0) {
        return 0;
    }

    final double d = Math.ceil(Math.log10(num < 0 ? -num: num));
    final int power = n - (int) d;

    double firstMagnitudeFactor = 1.0;
    double secondMagnitudeFactor = 1.0;
    if (power > maxPowerOfTen) {
        firstMagnitudeFactor = Math.pow(10.0, maxPowerOfTen);
        secondMagnitudeFactor = Math.pow(10.0, (double) power - maxPowerOfTen);
    } else {
        firstMagnitudeFactor = Math.pow(10.0, (double) power);
    }

    double toBeRounded = num * firstMagnitudeFactor;
    toBeRounded *= secondMagnitudeFactor;

    final long shifted = Math.round(toBeRounded);
    double rounded = ((double) shifted) / firstMagnitudeFactor;
    rounded /= secondMagnitudeFactor;
    return rounded;
}
7
Thomas Becker

Wie wäre es mit dieser Java-Lösung:

 double roundToSignificantFigure (doppelte Anzahl, int-Genauigkeit) {
 return new BigDecimal (num) 
 .round (neuer MathContext (Genauigkeit, RoundingMode.HALF_EVEN)) 
 .doubleValue (); 
} 
6

Hier ist eine modifizierte Version von Ates 'JavaScript, die negative Zahlen behandelt.

function sigFigs(n, sig) {
    if ( n === 0 )
        return 0
    var mult = Math.pow(10,
        sig - Math.floor(Math.log(n < 0 ? -n: n) / Math.LN10) - 1);
    return Math.round(n * mult) / mult;
 }
3
Jason Swank

Dies kam fünf Jahre zu spät, aber ich teile es mit anderen, die immer noch das gleiche Problem haben. Ich mag es, weil es einfach ist und keine Berechnungen auf der Codeseite. Siehe Eingebaute Methoden zum Anzeigen von signifikanten Zahlen für weitere Informationen.

Dies ist, wenn Sie es nur ausdrucken möchten.

public String toSignificantFiguresString(BigDecimal bd, int significantFigures){
    return String.format("%."+significantFigures+"G", bd);
}

Dies ist, wenn Sie es konvertieren möchten:

public BigDecimal toSignificantFigures(BigDecimal bd, int significantFigures){
    String s = String.format("%."+significantFigures+"G", bd);
    BigDecimal result = new BigDecimal(s);
    return result;
}

Hier ist ein Beispiel davon in Aktion:

BigDecimal bd = toSignificantFigures(BigDecimal.valueOf(0.0681), 2);
2
JackDev

[korrigiert, 2009-10-26]

Im Wesentlichen für N signifikante gebrochene Ziffern:

• Multiplizieren Sie die Zahl mit 10N
• Addiere 0,5
• Die Nachkommastellen abschneiden (d. H. Das Ergebnis in eine ganze Zahl abschneiden)
• Durch 10 teilenN

Für N signifikante integrale (nicht gebrochene) Ziffern:

• Teilen Sie die Zahl durch 10N
• Addiere 0,5
• Die Nachkommastellen abschneiden (d. H. Das Ergebnis in eine ganze Zahl abschneiden)
• Mit 10 multiplizierenN

Sie können dies auf jedem Rechner tun, der beispielsweise einen Operator "INT" (Integer-Kürzung) hat.

1
David R Tribble

JavaScript:

Number( my_number.toPrecision(3) );

Die Funktion Number ändert die Ausgabe der Form "8.143e+5" in "814300".

1
Zaz

Haben Sie versucht, es so zu programmieren, wie Sie es von Hand machen würden?

  1. Wandeln Sie die Zahl in eine Zeichenfolge um
  2. Beginnend am String zählen die Ziffern - führende Nullen sind nicht Bedeutsam, alles andere ist.
  3. Wenn Sie zur "n-ten" Ziffer kommen, schauen Sie mit der nächsten Ziffer nach ....... 5 oder mehr, runden Sie ab.
  4. Ersetzen Sie alle nachfolgenden Ziffern durch Nullen.
1
Mark Bessey

Hier ist der Pyrolistical-Code (derzeit die Top-Antwort) in Visual Basic.NET, falls es jemand benötigt:

Public Shared Function roundToSignificantDigits(ByVal num As Double, ByVal n As Integer) As Double
    If (num = 0) Then
        Return 0
    End If

    Dim d As Double = Math.Ceiling(Math.Log10(If(num < 0, -num, num)))
    Dim power As Integer = n - CInt(d)
    Dim magnitude As Double = Math.Pow(10, power)
    Dim shifted As Double = Math.Round(num * magnitude)
    Return shifted / magnitude
End Function
/**
 * Set Significant Digits.
 * @param value value
 * @param digits digits
 * @return
 */
public static BigDecimal setSignificantDigits(BigDecimal value, int digits) {
    //# Start with the leftmost non-zero digit (e.g. the "1" in 1200, or the "2" in 0.0256).
    //# Keep n digits. Replace the rest with zeros.
    //# Round up by one if appropriate.
    int p = value.precision();
    int s = value.scale();
    if (p < digits) {
        value = value.setScale(s + digits - p); //, RoundingMode.HALF_UP
    }
    value = value.movePointRight(s).movePointLeft(p - digits).setScale(0, RoundingMode.HALF_UP)
        .movePointRight(p - digits).movePointLeft(s);
    s = (s > (p - digits)) ? (s - (p - digits)) : 0;
    return value.setScale(s);
}
1
Valeri Shibaev

return new BigDecimal(value, new MathContext(significantFigures, RoundingMode.HALF_UP)).doubleValue();

0
Duncan Calvert

Dies ist einer, den ich in VB gefunden habe:

Function SF(n As Double, SigFigs As Integer)
    Dim l As Integer = n.ToString.Length
    n = n / 10 ^ (l - SigFigs)
    n = Math.Round(n)
    n = n * 10 ^ (l - SigFigs)
    Return n
End Function
0
SomeGuy

Ich brauchte dies in Go, was ein wenig kompliziert war, weil die Standardbibliothek der Go-Bibliothek math.Round() (vor go1.10) fehlte. Also musste ich das auch noch putzen. Hier ist meine Übersetzung von Pyrolisticals ausgezeichnete Antwort :

// TODO: replace in go1.10 with math.Round()
func round(x float64) float64 {
    return float64(int64(x + 0.5))
}

// SignificantDigits rounds a float64 to digits significant digits.
// Translated from Java at https://stackoverflow.com/a/1581007/1068283
func SignificantDigits(x float64, digits int) float64 {
    if x == 0 {
        return 0
    }

    power := digits - int(math.Ceil(math.Log10(math.Abs(x))))
    magnitude := math.Pow(10, float64(power))
    shifted := round(x * magnitude)
    return shifted / magnitude
}
0
Michael Hampton