任意の数値 (整数 > 0 だけでなく) を有効数字 N 桁に丸めるにはどうすればよいですか?
たとえば、有効数字 3 桁に丸めたい場合は、次のような式を探しています。
1,239,451 を返し、1,240,000 を返します
12.1257 と 12.1 を返します
.0681 とリターン .0681
5 して 5 を返す
当然のことながら、アルゴリズムは 3 の N のみを処理するようにハードコーディングされるべきではありませんが、それは出発点になります。
任意の数値 (整数 > 0 だけでなく) を有効数字 N 桁に丸めるにはどうすればよいですか?
たとえば、有効数字 3 桁に丸めたい場合は、次のような式を探しています。
1,239,451 を返し、1,240,000 を返します
12.1257 と 12.1 を返します
.0681 とリターン .0681
5 して 5 を返す
当然のことながら、アルゴリズムは 3 の N のみを処理するようにハードコーディングされるべきではありませんが、それは出発点になります。
他の回答にある 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;
}
短くて甘い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
まとめ:
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
だから、パイロの答えを使ってください!
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;
}
このJavaソリューションはどうですか:
double roundToSignificantFigure(double num, int precision){ 新しい BigDecimal(数値) を返す .round(new MathContext(precision, RoundingMode.HALF_EVEN)) .doubleValue(); }
これは、負の数を処理する 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;
}
[修正済み、2009-10-26]
基本的に、N個の重要な小数桁の場合:
•数値に10Nを掛け
ます•0.5を加算し
ます•小数桁を切り捨てます(つまり、結果を整数に切り捨てます)
• 10Nで割ります
N個の重要な整数(非小数)桁の場合:
•数値を10Nで除算し
ます•0.5を加算し
ます•小数桁を切り捨てます(つまり、結果を整数に切り捨てます) • 10N
を掛けます
これは、たとえば、「INT」(整数の切り捨て)演算子を持つ任意の計算機で実行できます。
これは、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);
}
public static double roundToSignificantDigits(double num, int n) {
return Double.parseDouble(new java.util.Formatter().format("%." + (n - 1) + "e", num).toString());
}
このコードは、丸め関数に変換される組み込みの書式設定関数を使用します