모든 숫자 (정수> 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
했으며 긴 중간을 더 명확하게했습니다.
이 버그는 큰 수에 작은 수를 곱하여 발생했습니다. 대신 비슷한 크기의 두 숫자를 나눕니다.
편집은
더 많은 버그가 수정되었습니다. 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
n==0
:
Math.log(n) / Math.LN10
대신 해야 할 이유 가 Math.log10(n)
있습니까?
Math.floor(x) == Math.ceil(x) - 1
있습니까 , 아니면이 대답이 가정 합니까? 언제 x
가 정수가 아니기 때문 입니다. pow
함수 의 두 번째 인수는 다음과 같아야합니다 sig - Math.ceil(Math.log(n) / Math.LN10)
(또는 그냥 사용 Math.log10
)
요약:
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
Number(814301).toPrecision(4) == "8.143e+5"
. 일반적으로 이것을 사용자에게 보여 주면 원하는 것이 아닙니다.
Pyrolistical의 (매우 좋은!) 솔루션에는 여전히 문제가 있습니다. Java의 최대 double 값은 10 ^ 308 정도이고 최소값은 10 ^ -324 정도입니다. 따라서 roundToSignificantFigures
.NET의 몇 제곱 이내 인 것에 함수 를 적용 할 때 문제가 발생할 수 있습니다 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;
}
이것은 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);
[2009 년 10 월 26 일 수정]
기본적으로 N 개의 유효 소수 자릿수 :
• 숫자에 10 N을 곱합니다.
• 0.5를 더합니다.
• 분수 자릿수를 자릅니다 (즉, 결과를 정수로 자름).
• 10 N 으로 나눕니다.
N 개의 유효 정수 (비 분수) 자릿수 :
• 숫자를 10 N
으로 나눕니다. •
0.5를 더합니다.
• 분수 자릿수를 자릅니다 (즉, 결과를 정수로 자름).
• 10 N을 곱합니다.
예를 들어 "INT"(정수 잘림) 연산자가있는 모든 계산기에서이 작업을 수행 할 수 있습니다.
/**
* 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);
}
다음은 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
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
}
FloatToStrF를 사용하여 10의 거듭 제곱 등으로 이러한 모든 계산을 피할 수 있습니다.
FloatToStrF를 사용하면 출력 된 값 (문자열이 됨)에서 정밀도 (유효 숫자 수)를 선택할 수 있습니다. 물론 여기에 StrToFloat를 적용하여 반올림 된 값을 float로 얻을 수 있습니다.
여길 봐:
public static double roundToSignificantDigits(double num, int n) {
return Double.parseDouble(new java.util.Formatter().format("%." + (n - 1) + "e", num).toString());
}
이 코드는 반올림 기능으로 전환되는 내장 서식 지정 기능을 사용합니다.