[algorithm] 임의의 유효 자릿수로 반올림
모든 숫자 (정수> 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
.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;
}
답변
이 자바 솔루션은 어떻습니까?
double roundToSignificantFigure (double num, int precision) { new BigDecimal (num) 반환 .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;
}