반올림하여 임의의 숫자를 중요한 자리 숫자
-
03-07-2019 - |
문제
할 수 있는 방법 당신은 라운드 모 수(다만 정수>0)N 뜻깊은 자리?
예를 들어,내가 원하는 경우 라운드를 세 가지 중요한 자리 숫자,내가 찾는 수식을 취할 수 있:
1,239,451 및 반환 1,240,000
12.1257 및 반환 12.1
.0681 및 반환 .0681
5 고 돌아 5
자연적으로는 알고리즘하지 않아야 하드 코딩을 처리 N 의 3 지만,그 시작이 될 것 이다입니다.
해결책
다음은 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);
}
그래서 당신은를 찾을 필요가 소수점 첫 번째로 자리,저장한 다음 다음 N-1 숫자,그 라운드의 N 번째 자리에 기초합니다.
우리가 사용할 수 있습니다 로그인을 첫 번째입니다.
log 1239451 = 6.09
log 12.1257 = 1.08
log 0.0681 = -1.16
그래서에 대한 숫자>0,취 ceil 의 로그를 보실 수 있습니다.에 대한 숫자 < 0,취할 바닥의 로그를 보실 수 있습니다.
이제 우리는 숫자 d
:7 에서 첫번째 경우 2 2-2in the3rd.
우리가하 라운드 (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
그래서 사용하는 파이로가의 대답이다!
"짧고 달콤한"JavaScript 구현이 아닙니다
Number(n).toPrecision(sig)
예를 들어
alert(Number(12345).toPrecision(3)
?
죄송합니다. 여기서는 멍청하지 않습니다. Claudiu의 "Roundit"기능을 사용하고 JavaScript의 .Teprecision을 사용하면 다른 결과를 얻을 수 있지만 마지막 숫자의 반올림만으로는 나에게만 결과가 나됩니다.
자바 스크립트 :
Number(8.14301).toPrecision(4) == 8.143
.그물
roundit(8.14301,4) == 8.144
Pyrolistical의 (매우 좋은!) 솔루션에는 여전히 문제가 있습니다. Java의 최대 이중 값은 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;
}
이 Java 솔루션은 어떻습니까 :
double roundToSignificantFigure(double num, int precision){ return 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;
}
이것은 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);
손으로하는 방식으로 코딩하는 것만 시도해 보셨습니까?
- 숫자를 문자열로 변환하십시오
- 문자열의 시작 부분에서 시작하여 카운트 숫자 - 선행 제로는 중요하지 않으며 다른 모든 것이 있습니다.
- "Nth"숫자에 도착하면 다음 숫자에서 앞서 나가고 5 개 이상이라면 반올림하십시오.
- 모든 후행 숫자를 0으로 교체하십시오.
교정, 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
자바 스크립트 :
Number( my_number.toPrecision(3) );
그만큼 Number
함수는 양식의 출력을 변경합니다 "8.143e+5"
에게 "814300"
.
이것은 내가 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();
나는 이것을 Go에서 필요로했는데, Go Standard Library의 부족으로 인해 약간 복잡했습니다. 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
}
public static double roundToSignificantDigits(double num, int n) {
return Double.parseDouble(new java.util.Formatter().format("%." + (n - 1) + "e", num).toString());
}
이 코드는 내장 형식 기능을 사용하여 반올림 함수로 전환됩니다.