web-dev-qa-db-ja.com

任意の有効桁数への丸め

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

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

1,239,451およびリターン1,240,000

12.1257および12.1を返す

.0681および戻り.0681

5および5を返す

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

79
DougN

Javaに同じコードがありますが、他の回答には12.100000000000001のバグはありません

また、繰り返しコードを削除し、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;
}
101
Pyrolistical

概要:

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)th桁を丸める必要があります。何かのようなもの:

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

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

15
Claudiu

これは短くて便利な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
15
Ates Goral

「短くて甘い」JavaScript実装ではない

Number(n).toPrecision(sig)

例えば.

alert(Number(12345).toPrecision(3)

申し訳ありませんが、私はここでファセットをしているわけではありません。Claudiuの "roundit"関数とJavaScriptの.toPrecisionを使用すると異なる結果が得られますが、最後の桁を丸めるだけです。

JavaScript:

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

。ネット

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

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

roundToSignificantFigures(1.234E-310, 3);

変数powerの値は3-(-309)= 312になります。その結果、変数magnitudeInfinityになり、それ以降はすべてゴミになります。幸いなことに、これは克服できない問題ではありません。オーバーフローしているのはfactormagnitudeだけです。本当に重要なのはproductnum * 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;
}
7
Thomas Becker

これはどうですかJava solution:

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

以下は、負の数を処理する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;
 }
3
Jason Swank

これは5年遅れでしたが、まだ同じ問題を抱えている他の人たちと共有しますが。シンプルであり、コード側で計算がないため、気に入っています。詳細については、 重要な数字を表示するためのメソッドに組み込まれている を参照してください。

これは、印刷するだけの場合です。

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

これは変換したい場合です:

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

動作中の例を次に示します。

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

[修正、2009-10-26]

本質的に、有効なNの場合小数桁:

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

Nの有効な整数(非分数)桁の場合:

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

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

1
David R Tribble

JavaScript:

Number( my_number.toPrecision(3) );

Number関数は、"8.143e+5"の形式の出力を"814300"に変更します。

1
Zaz

手作業で行う方法でコーディングしてみましたか?

  1. 数値を文字列に変換します
  2. 文字列の先頭から数を数えます-先行ゼロは重要ではありませんが、他のすべては重要です。
  3. 「n番目」の桁に到達したら、次の桁を先読みし、5桁以上であれば切り上げます。
  4. 末尾の数字をすべてゼロに置き換えます。
1
Mark Bessey

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
/**
 * 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

これは私がVBで思いついたものです:

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

Goでこれが必要でしたが、Go標準ライブラリにmath.Round()がない(go1.10より前)ため、少し複雑になりました。だから私もそれを盛り上げなければなりませんでした。 Pyrolisticalの優れた答え の私の翻訳です:

// 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