Frage

Wie können Sie rund alle Nummer (nicht nur ganze Zahlen> 0) zu N signifikanten Stellen?

Zum Beispiel, wenn ich auf drei signifikante Stellen runden mag, ich bin auf der Suche nach einer Formel, das nehmen könnte:

1.239.451 und 1.240.000 zurückkehren

12,1257 und zurück 12.1

0,0681 und 0,0681 zurückkehren

5 und zurück 5

Natürlich sollte der Algorithmus nicht hartcodiert werden, um nur zu behandeln N von 3, obwohl das ein Anfang sein würde.

War es hilfreich?

Lösung

Hier ist der gleiche Code in Java ohne die 12,100000000000001 Bug anderen Antworten haben

Ich entfernte auch wiederholt Code geändert power auf einen Typ integer zu verhindern Probleme floating wenn n - d geschehen ist, und machte die lange Zwischen mehr klar

Der Fehler wurde verursacht durch eine große Anzahl mit einer kleinen Zahl multipliziert wird. Stattdessen teile ich zwei Zahlen von ähnlicher Größe.

Bearbeiten
Feste mehr Fehler. Hinzugefügt Check für 0, wie es in NaN führen würde. Aus der Funktion tatsächlich mit negativen Zahlen arbeitet (Der ursprüngliche Code behandelt nicht negative Zahlen, 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;
}

Andere Tipps

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

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);
}

So Sie die Dezimalstelle der ersten Nicht-Null-Zahl finden müssen, dann speichern Sie den nächsten N-1-stellig, dann rund um die n-te Ziffer basierend auf dem Rest.

Wir verwenden anmelden, um die ersten zu tun.

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

Also für Zahlen> 0, nehmen Sie die ceil des Protokolls. Für Zahlen <0, nehmen Sie den Boden des Protokolls.

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

Wir haben die (d-N)th Ziffer abzurunden. 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 die Standardrundung etwas tun:

roundedrest = (int)(roundedrest + 0.5);

Und die pow rückgängig gemacht werden.

roundednum = pow(roundedrest, -(power))

Wo Macht ist die Macht oben berechnet.


Über Genauigkeit: Pyrolistical Antwort ist in der Tat näher an der realen Ergebnis. Aber beachten Sie, dass Sie nicht 12.1 genau in jedem Fall darstellen kann. Wenn Sie die Antworten wie folgt drucken:

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

Die Antworten sind:

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

So verwenden Pyro Antwort!

Ist das nicht die "kurz und bündig" JavaScript-Implementierung

Number(n).toPrecision(sig)

z.

alert(Number(12345).toPrecision(3)

Sorry, ich meine es hier nicht spöttisch, es ist nur, dass die „Roundit“ -Funktion von Claudiu und der .toPrecision in JavaScript verwenden gibt mir unterschiedliche Ergebnisse, aber nur in der Rundung der letzten Stelle.

JavaScript:

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

.NET

roundit(8.14301,4) == 8.144

Pyrolistical ist (sehr schön!) Lösung hat immer noch ein Problem. Der maximale Doppelwert in Java ist in der Größenordnung von 10 ^ 308, wobei der Minimalwert in der Größenordnung von 10 ^ -324 ist. Daher können Sie in Schwierigkeiten geraten, wenn die Funktion roundToSignificantFigures auf etwas anwenden, die innerhalb von wenigen Zehnerpotenzen von Double.MIN_VALUE ist. Zum Beispiel, wenn Sie anrufen

roundToSignificantFigures(1.234E-310, 3);

, dann wird die Variable power den Wert 3 haben - (-309) = 312. Folglich wird der Variable magnitude Infinity wird, und es ist alles Müll von da an aus. Glücklicherweise ist dies kein unüberwindbares Problem: es ist nur die Faktor magnitude, die überquell ist. Was wirklich zählt, ist die Produkt num * magnitude, und das nicht überläuft. Eine Möglichkeit, dies zu lösen, ist durch die Multiplikation mit dem Faktor magintude in zwei Schritte Zerschlagung:


 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;
}

Wie über diese Java-Lösung:

double roundToSignificantFigure(double num, int precision){
 return new BigDecimal(num)
            .round(new MathContext(precision, RoundingMode.HALF_EVEN))
            .doubleValue(); 
}

Hier ist eine modifizierte Version von Ates' JavaScript, die negativen 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;
 }

Das kam spät 5 Jahre, aber wenn ich für andere noch das gleiche Problem mit teilen werde. Ich mag es, weil es einfach ist und keine Berechnungen auf der Code-Seite. Siehe gebaut Methoden zur Darstellung von Signifikante Zahlen mehr info.

Dies ist, wenn Sie es nur drucken möchten aus.

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);

Haben Sie versucht Codierung es nur den Weg nach oben Sie es von Hand tun würde?

  1. Konvertieren Sie die Zahl in einen String
  2. Ab Anfang der string, count digits - führende Nullen sind nicht signifikant, alles andere ist.
  3. Wenn Sie auf den „n-te“ digit bekommen, peek voraus auf die nächste Stelle und wenn es ist 5 oder höher, aufrunden.
  4. Ersetzen Sie alle die hinteren Ziffern mit Nullen.

[Korrigierte, 2009-10-26]

Im Wesentlichen für N signifikant fraktionierte Zahlen:

• Multiplizieren Sie die Zahl von 10 N
• In 0,5
• die Fraktion Ziffern Kürzen (d.h. das Ergebnis in eine ganze Zahl trunkieren)
• Teile von 10 N

Für N signifikant Integral (nicht-fraktionierte) Ziffern:

• Teilen Sie die Zahl durch 10 N
• In 0,5
• die Fraktion Ziffern Kürzen (d.h. das Ergebnis in eine ganze Zahl trunkieren)
• Multipliziert mit 10 N

Sie können dies tun, auf jedem Rechner, zum Beispiel, dass ein "INT" (integer Trunkierung) Operator hat.

/**
 * 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);
}

Hier ist Pyrolistical des (derzeit Top-Antwort) Code in Visual Basic.NET, sollte jemand braucht es:

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

JavaScript:

Number( my_number.toPrecision(3) );

Die Number Funktion Ausgabe des Formulars "8.143e+5" zu "814300" ändern.

Dies ist eine, die ich mit in VB kam:

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

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

brauchte ich dies in Go, das war ein bisschen durch die fehlende Standardbibliothek Go kompliziert von math.Round() (vor go1.10). Also musste ich auch, dass bis peitschen. Hier ist meine Übersetzung von Pyrolistical die 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
}
public static double roundToSignificantDigits(double num, int n) {
    return Double.parseDouble(new java.util.Formatter().format("%." + (n - 1) + "e", num).toString());
}

Dieser Code verwendet die integrierte Formatierungsfunktion, die zu einer Rundungsfunktion eingeschaltet ist

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top