임의의 유효 자릿수로 반올림
모든 숫자 (정수> 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
했으며 긴 중간을 더 명확하게했습니다.
이 버그는 큰 수에 작은 수를 곱하여 발생했습니다. 대신 비슷한 크기의 두 숫자를 나눕니다.
편집은
더 많은 버그가 수정되었습니다. 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);
}
따라서 0이 아닌 첫 번째 숫자의 소수점 자리를 찾은 다음 다음 N-1 숫자를 저장 한 다음 나머지를 기준으로 N 번째 숫자를 반올림해야합니다.
먼저 로그를 사용할 수 있습니다.
log 1239451 = 6.09
log 12.1257 = 1.08
log 0.0681 = -1.16
따라서 숫자가 0보다 크면 로그의 천장을 취하십시오. 숫자가 0 미만인 경우 로그의 바닥을 차지합니다.
이제 숫자가 있습니다 d
. 첫 번째 경우 7, 두 번째 경우 2, 세 번째 경우 -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))
전력은 위에서 계산 된 전력입니다.
정확성에 관하여 : Pyrolistical의 대답은 실제로 실제 결과에 더 가깝습니다. 그러나 어떤 경우에도 12.1을 정확하게 나타낼 수는 없습니다. 다음과 같이 답변을 인쇄하는 경우 :
System.out.println(new BigDecimal(n));
답은 다음과 같습니다.
Pyro's: 12.0999999999999996447286321199499070644378662109375
Mine: 12.10000000000000142108547152020037174224853515625
Printing 12.1 directly: 12.0999999999999996447286321199499070644378662109375
따라서 Pyro의 대답을 사용하십시오!
"짧고 달콤한"자바 스크립트 구현이 아님
Number(n).toPrecision(sig)
예 :
alert(Number(12345).toPrecision(3)
?
죄송합니다. 저는 여기서 우스꽝스럽지 않습니다. Claudiu의 "roundit"함수와 JavaScript의 .toPrecision을 사용하면 다른 결과를 얻을 수 있지만 마지막 숫자 만 반올림 할 수 있습니다.
자바 스크립트 :
Number(8.14301).toPrecision(4) == 8.143
.그물
roundit(8.14301,4) == 8.144
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
이며 넘치지 않습니다. 이 문제를 해결하는 한 가지 방법은 곱셈 magintude
을 두 단계로 나누는 것입니다.
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;
}
How about this java solution :
double roundToSignificantFigure(double num, int precision){ return new BigDecimal(num) .round(new MathContext(precision, RoundingMode.HALF_EVEN)) .doubleValue(); }
Here is a modified version of Ates' JavaScript that handles negative numbers.
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;
}
This came 5 years late, but though I'll share for others still having the same issue. I like it because it's simple and no calculations on the code side. See Built in methods for displaying Significant figures for more info.
This is if you just want to print it out.
public String toSignificantFiguresString(BigDecimal bd, int significantFigures){
return String.format("%."+significantFigures+"G", bd);
}
This is if you want to convert it:
public BigDecimal toSignificantFigures(BigDecimal bd, int significantFigures){
String s = String.format("%."+significantFigures+"G", bd);
BigDecimal result = new BigDecimal(s);
return result;
}
Here's an example of it in action:
BigDecimal bd = toSignificantFigures(BigDecimal.valueOf(0.0681), 2);
Have you tried just coding it up the way you'd do it by hand?
- Convert the number to a string
- Starting at the beginning of the string, count digits - leading zeroes aren't significant, everything else is.
- When you get to the "nth" digit, peek ahead at the next digit and if it's 5 or higher, round up.
- Replace all of the trailing digits with zeroes.
[Corrected, 2009-10-26]
Essentially, for N significant fractional digits:
• Multiply the number by 10N
• Add 0.5
• Truncate the fraction digits (i.e., truncate the result into an integer)
• Divide by 10N
For N significant integral (non-fractional) digits:
• Divide the number by 10N
• Add 0.5
• Truncate the fraction digits (i.e., truncate the result into an integer)
• Multiply by 10N
You can do this on any calculator, for example, that has an "INT" (integer truncation) operator.
/**
* 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);
}
Here is Pyrolistical's (currently top answer) code in Visual Basic.NET, should anyone need it:
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) );
The Number
function will change output of the form "8.143e+5"
to "814300"
.
This is one that I came up with in 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
return new BigDecimal(value, new MathContext(significantFigures, RoundingMode.HALF_UP)).doubleValue();
I needed this in Go, which was a bit complicated by the Go standard library's lack of math.Round()
(before go1.10). So I had to whip that up too. Here is my translation of Pyrolistical's excellent answer:
// 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());
}
This code uses the inbuilt formatting function which is turned to a rounding function
참고URL : https://stackoverflow.com/questions/202302/rounding-to-an-arbitrary-number-of-significant-digits
'Development Tip' 카테고리의 다른 글
Swift 프로젝트에서 Objective-C CocoaPods를 사용하는 방법 (0) | 2020.10.09 |
---|---|
Typescript : TS7006 : 매개 변수 'xxx'에는 암시 적으로 'any'유형이 있습니다. (0) | 2020.10.09 |
C에서 최고의 타이밍 방법? (0) | 2020.10.09 |
MySQL : 사용자가 있는지 확인하고 삭제하십시오. (0) | 2020.10.09 |
C ++를 사용하여 런타임에 메모리 사용량을 얻는 방법은 무엇입니까? (0) | 2020.10.09 |