84

任意の数値 (整数 > 0 だけでなく) を有効数字 N 桁に丸めるにはどうすればよいですか?

たとえば、有効数字 3 桁に丸めたい場合は、次のような式を探しています。

1,239,451 を返し、1,240,000 を返します

12.1257 と 12.1 を返します

.0681 とリターン .0681

5 して 5 を返す

当然のことながら、アルゴリズムは 3 の N のみを処理するようにハードコーディングされるべきではありませんが、それは出発点になります。

4

18 に答える 18

107

他の回答にある 12.100000000000001 バグのない Java の同じコードを次に示します。

また、繰り返されるコードを削除し、完了power時の浮動の問題を防ぐために整数型に変更しn - d、長い中間をより明確にしました

このバグは、大きな数に小さな数を掛けることによって引き起こされました。代わりに、同じ大きさの 2 つの数を割ります。

EDIT
より多くのバグを修正しました。NaN になるため、0 のチェックを追加しました。関数が実際に負の数で動作するようにしました (負の数の対数は複素数であるため、元のコードでは負の数を処理しません)。

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;
}
于 2009-10-17T00:20:15.390 に答える
16

短くて甘いJavaScriptの実装は次のとおりです。

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
于 2008-10-14T19:20:32.587 に答える
15

まとめ:

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

したがって、ゼロ以外の最初の数字の小数点以下の桁数を見つけて、次の N-1 桁を保存し、残りに基づいて N 桁目を丸める必要があります。

ログを使用して最初の作業を行うことができます。

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

したがって、数値が 0 より大きい場合は、ログの上限を取得します。数値が 0 未満の場合は、丸太の床を取ります。

これで数字が得られましたd: 最初のケースでは 7、2 番目のケースでは 2、3 番目のケースでは -2 です。

(d-N)番目の桁を四捨五入する必要があります。何かのようなもの:

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

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

次に、標準の丸め処理を行います。

roundedrest = (int)(roundedrest + 0.5);

そして、パウを元に戻します。

roundednum = pow(roundedrest, -(power))

power は上記で計算された電力です。


精度について: Pyrolistical の回答は実際の結果により近いものです。ただし、どのような場合でも 12.1 を正確に表すことはできないことに注意してください。次のように回答を印刷すると:

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

答えは次のとおりです。

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

だから、パイロの答えを使ってください!

于 2008-10-14T18:47:01.700 に答える
8

Pyrolistical の (非常に素晴らしい!) ソリューションにはまだ問題があります。Java の double の最大値は 10^308 のオーダーで、最小値は 10^-324 のオーダーです。したがって、関数roundToSignificantFiguresを の 10 の数乗以内にあるものに適用すると、問題が発生する可能性がありDouble.MIN_VALUEます。たとえば、あなたが電話をかけるとき

roundToSignificantFigures(1.234E-310, 3);

その場合、変数powerの値は 3 - (-309) = 312 になります。その結果、変数magnitudeは になりInfinity、それ以降はすべてゴミになります。幸いなことに、これは克服できない問題ではありません。オーバーフローしているのは要因だけです。 magnitude本当に重要なのは製品 num * magnitudeであり、それはオーバーフローしません。これを解決する 1 つの方法は、係数による乗算magintudeを 2 つのステップに分割することです。


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

于 2010-11-19T01:17:06.600 に答える
6

このJavaソリューションはどうですか:

double roundToSignificantFigure(double num, int precision){
 新しい BigDecimal(数値) を返す
            .round(new MathContext(precision, RoundingMode.HALF_EVEN))
            .doubleValue();
}
于 2010-08-10T08:00:42.033 に答える
3

これは、負の数を処理する Ates の JavaScript の修正版です。

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;
 }
于 2010-06-04T16:18:40.653 に答える
1

[修正済み、2009-10-26]

基本的に、N個の重要な数桁の場合:

•数値に10Nを掛け
ます•0.5を加算し
ます•小数桁を切り捨てます(つまり、結果を整数に切り捨てます)
10Nで割ります

N個の重要な整数(非小数)桁の場合:

•数値を10Nで除算し
ます•0.5を加算し
ます•小数桁を切り捨てます(つまり、結果を整数に切り捨てます) • 10N
を掛けます

これは、たとえば、「INT」(整数の切り捨て)演算子を持つ任意の計算機で実行できます。

于 2009-10-17T00:41:19.240 に答える
1

これは、Visual Basic .NET の Pyrolistical の (現在の最高の回答) コードです。

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
于 2011-06-21T16:10:45.743 に答える
1

手で行うのと同じようにコーディングしてみましたか?

  1. 数値を文字列に変換する
  2. 文字列の先頭から数えて、先頭のゼロは重要ではなく、それ以外はすべて重要です。
  3. 「n 番目」の桁に到達したら、次の桁を見て、5 以上の場合は切り上げます。
  4. 末尾の数字をすべてゼロに置き換えます。
于 2008-10-14T18:52:37.913 に答える
1
/**
 * 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);
}
于 2010-06-22T00:25:30.727 に答える
-1
public static double roundToSignificantDigits(double num, int n) {
    return Double.parseDouble(new java.util.Formatter().format("%." + (n - 1) + "e", num).toString());
}

このコードは、丸め関数に変換される組み込みの書式設定関数を使用します

于 2013-05-19T05:51:47.980 に答える