Java Language
Java 부동 소수점 연산
수색…
소개
부동 소수점 숫자는 소수 부분이있는 숫자입니다 (일반적으로 소수점으로 표시). Java에서는 float
(4 바이트 사용) 및 double
(8 바이트 사용) 인 부동 소수점 숫자에 대해 두 가지 기본 유형이 있습니다. 이 문서 페이지는 Java의 부동 소수점에서 수행 할 수있는 예제 작업을 자세히 설명합니다.
부동 소수점 값 비교
관계 연산자를 사용하여 부동 소수점 값 ( float
또는 double
)을 비교할 때주의해야합니다 ( ==
!=
, <
등). 이 연산자는 부동 소수점 값의 이진 표현에 따라 결과를 제공합니다. 예 :
public class CompareTest {
public static void main(String[] args) {
double oneThird = 1.0 / 3.0;
double one = oneThird * 3;
System.out.println(one == 1.0); // prints "false"
}
}
oneThird
계산은 작은 반올림 오류를 가져 oneThird
에 3
을 곱하면 1.0
약간 다른 결과를 얻습니다.
정확하지 않은 표현에 대한이 문제는 계산에서 double
과 float
을 섞으려고 할 때 더욱 분명해진다. 예 :
public class CompareTest2 {
public static void main(String[] args) {
float floatVal = 0.1f;
double doubleVal = 0.1;
double doubleValCopy = floatVal;
System.out.println(floatVal); // 0.1
System.out.println(doubleVal); // 0.1
System.out.println(doubleValCopy); // 0.10000000149011612
System.out.println(floatVal == doubleVal); // false
System.out.println(doubleVal == doubleValCopy); // false
}
}
자바에서 사용되는 부동 소수점 표현 float
과 double
종류의 정밀도 숫자의 제한된 수있다. float
유형의 경우 정밀도는 23 진수 또는 약 8 자리입니다. double
유형의 경우 52 비트 또는 약 15 자리 10 진수입니다. 그 중 일부 산술 연산은 반올림 오류를 발생시킵니다. 따라서 프로그램이 부동 소수점 값을 비교할 때 비교할 수있는 델타 를 정의하는 것이 표준 방법입니다. 두 숫자의 차이가 델타보다 작 으면 델타는 동일하다고 간주됩니다. 예를 들어
if (Math.abs(v1 - v2) < delta)
델타 비교 예 :
public class DeltaCompareExample {
private static boolean deltaCompare(double v1, double v2, double delta) {
// return true iff the difference between v1 and v2 is less than delta
return Math.abs(v1 - v2) < delta;
}
public static void main(String[] args) {
double[] doubles = {1.0, 1.0001, 1.0000001, 1.000000001, 1.0000000000001};
double[] deltas = {0.01, 0.00001, 0.0000001, 0.0000000001, 0};
// loop through all of deltas initialized above
for (int j = 0; j < deltas.length; j++) {
double delta = deltas[j];
System.out.println("delta: " + delta);
// loop through all of the doubles initialized above
for (int i = 0; i < doubles.length - 1; i++) {
double d1 = doubles[i];
double d2 = doubles[i + 1];
boolean result = deltaCompare(d1, d2, delta);
System.out.println("" + d1 + " == " + d2 + " ? " + result);
}
System.out.println();
}
}
}
결과:
delta: 0.01
1.0 == 1.0001 ? true
1.0001 == 1.0000001 ? true
1.0000001 == 1.000000001 ? true
1.000000001 == 1.0000000000001 ? true
delta: 1.0E-5
1.0 == 1.0001 ? false
1.0001 == 1.0000001 ? false
1.0000001 == 1.000000001 ? true
1.000000001 == 1.0000000000001 ? true
delta: 1.0E-7
1.0 == 1.0001 ? false
1.0001 == 1.0000001 ? false
1.0000001 == 1.000000001 ? true
1.000000001 == 1.0000000000001 ? true
delta: 1.0E-10
1.0 == 1.0001 ? false
1.0001 == 1.0000001 ? false
1.0000001 == 1.000000001 ? false
1.000000001 == 1.0000000000001 ? false
delta: 0.0
1.0 == 1.0001 ? false
1.0001 == 1.0000001 ? false
1.0000001 == 1.000000001 ? false
1.000000001 == 1.0000000000001 ? false
또한 double
및 float
기본 유형을 비교하기 위해 해당하는 복싱 유형의 정적 compare
메소드를 사용할 수 있습니다. 예 :
double a = 1.0;
double b = 1.0001;
System.out.println(Double.compare(a, b));//-1
System.out.println(Double.compare(b, a));//1
마지막으로 어떤 델타가 비교에 가장 적합한 지 결정하는 것은 까다로울 수 있습니다. 일반적으로 사용되는 접근법은 델타 값을 선택하는 것입니다. 우리의 직감은 올바른 것이라고 생각합니다. 그러나 입력 값의 정확성과 정확성을 확인하고 수행 된 계산을 알고있는 경우 결과의 정확성 및 따라서 델타에 대해 수학적으로 건전한 경계를 제시 할 수 있습니다. (이 종류의 분석을 다루는 계산 과학자들에게 가르쳐 지곤했던 Numerical Analysis로 알려진 공식적인 수학 분과가 있습니다.)
오버 플로우 및 언더 플로우
부동 데이터 유형
float 데이터 유형은 단 정밀도 32 비트 IEEE 754 부동 소수점입니다.
Float
오버플로
가능한 최대 값은 3.4028235e+38
이며,이 값을 초과하면 Infinity
생성됩니다.
float f = 3.4e38f;
float result = f*2;
System.out.println(result); //Infinity
Float
언더 플로우
최소값은 1.4e-45f이며,이 값보다 작아지면 0.0
을 생성합니다.
float f = 1e-45f;
float result = f/1000;
System.out.println(result);
이중 데이터 유형
double 데이터 형식은 배정 밀도 64-bit
IEEE 754 부동 소수점 형식입니다.
Double
오버 플로우
가능한 최대 값은 1.7976931348623157e+308
값을 초과하면 Infinity
생성됩니다.
double d = 1e308;
double result=d*2;
System.out.println(result); //Infinity
Double
언더 플로우
최소값은 4.9e-324이고,이 값보다 작아지면 0.0
을 생성합니다.
double d = 4.8e-323;
double result = d/1000;
System.out.println(result); //0.0
부동 소수점 값 형식화
부동 소수점 숫자는 'f'
플래그가있는 String.format
을 사용하여 10 진수로 형식을 지정할 수 있습니다.
//Two digits in fracttional part are rounded
String format1 = String.format("%.2f", 1.2399);
System.out.println(format1); // "1.24"
// three digits in fractional part are rounded
String format2 = String.format("%.3f", 1.2399);
System.out.println(format2); // "1.240"
//rounded to two digits, filled with zero
String format3 = String.format("%.2f", 1.2);
System.out.println(format3); // returns "1.20"
//rounder to two digits
String format4 = String.format("%.2f", 3.19999);
System.out.println(format4); // "3.20"
부동 소수점 숫자는 DecimalFormat
사용하여 10 진수로 서식을 지정할 수 있습니다.
// rounded with one digit fractional part
String format = new DecimalFormat("0.#").format(4.3200);
System.out.println(format); // 4.3
// rounded with two digit fractional part
String format = new DecimalFormat("0.##").format(1.2323000);
System.out.println(format); //1.23
// formatting floating numbers to decimal number
double dv = 123456789;
System.out.println(dv); // 1.23456789E8
String format = new DecimalFormat("0").format(dv);
System.out.println(format); //123456789
IEEE 사양에 대한 엄격한 준수
기본적으로 float
및 double
에 대한 부동 소수점 연산은 IEEE 754 사양의 규칙을 엄격하게 준수 하지 않습니다 . 표현식은 이러한 값의 범위에 대한 구현 별 확장을 사용할 수 있습니다. 본질적으로 그것들이 요구되는 것보다 더 정확한 것을 허용한다.
strictfp
이 동작을 비활성화합니다. 그것은 클래스, 인터페이스, 또는 방법에 적용하고, 이러한 등등 클래스, 인터페이스, 메소드, 생성자, 변수 초기화로서, 그 안에 포함 된 모든 적용된다 strictfp
, 부동 소수점 표현의 중간 값 내에 있어야 float 값 세트 또는 double 치 세트 결과적으로 이러한 표현식의 결과는 IEEE 754 사양에서 예측 한 결과와 동일합니다.
모든 상수 표현식은 strictfp
범위 내에 있지 않더라도 암묵적으로 엄격합니다.
따라서 strictfp
는 때로는 특정 코너 케이스 계산을 정확 하지 않게 만드는 그물 효과를 가지며 부동 소수점 연산을 느리게 만들 수도 있습니다 (CPU가 현재 네이티브 추가 정밀도가 결과에 영향을 미치지 않도록 더 많은 작업을 수행함). 그러나 모든 플랫폼에서 결과가 동일하게됩니다. 따라서 재현성이 속도보다 더 중요한 과학 프로그램과 같은 것들에 유용합니다.
public class StrictFP { // No strictfp -> default lenient
public strictfp float strict(float input) {
return input * input / 3.4f; // Strictly adheres to the spec.
// May be less accurate and may be slower.
}
public float lenient(float input) {
return input * input / 3.4f; // Can sometimes be more accurate and faster,
// but results may not be reproducable.
}
public static final strictfp class Ops { // strictfp affects all enclosed entities
private StrictOps() {}
public static div(double dividend, double divisor) { // implicitly strictfp
return dividend / divisor;
}
}
}