[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가됩니다. 결과적으로 변수 magnitudeInfinity이고 그 이후부터는 모두 쓰레기가됩니다. 다행히도 이것은 극복 할 수없는 문제가 아닙니다 . 넘쳐나 는 요인 일뿐 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;
 }