다음 예제에서 0 (또는 무한대)으로 나눌 수 있습니까?
public double calculation(double a, double b)
{
if (a == b)
{
return 0;
}
else
{
return 2 / (a - b);
}
}
일반적인 경우에는 물론 그렇지 않습니다. 그러나 경우 a
와 b
매우 가까운 수 있습니다 (a-b)
되는 결과 0
계산의 정밀도 때문에?
이 질문은 Java에 관한 것이지만 대부분의 프로그래밍 언어에 적용될 것이라고 생각합니다.
답변
Java에서는 if와 a - b
같지 않습니다 . Java는 비정규 화 된 숫자를 지원하는 IEEE 754 부동 소수점 연산을 요구하기 때문입니다. 로부터 사양 :0
a != b
특히, Java 프로그래밍 언어는 IEEE 754 비정규 부동 소수점 숫자 및 점진적 언더 플로우를 지원해야하므로 특정 숫자 알고리즘의 바람직한 특성을 쉽게 입증 할 수 있습니다. 계산 결과가 비정규 화 된 숫자 인 경우 부동 소수점 연산은 “0으로 플러시”되지 않습니다.
만약에 FPU 가 비정규 화 된 숫자 와 함께 작동 다른 숫자를 빼면 절대 곱셈과 달리 절대 0이 될 수 없습니다 . 이 질문 도 참조하십시오 .
다른 언어의 경우 다릅니다. 예를 들어 C 또는 C ++에서 IEEE 754 지원은 선택 사항입니다.
즉, 상기 것이 가능 표현식 2 / (a - b)
으로 예를 들면, 오버 플로우 a = 5e-308
및b = 4e-308
.
답변
해결 방법으로 다음은 어떻습니까?
public double calculation(double a, double b) {
double c = a - b;
if (c == 0)
{
return 0;
}
else
{
return 2 / c;
}
}
그렇게하면 어떤 언어로든 IEEE 지원에 의존하지 않습니다.
답변
a - b
부동 소수점을 0으로 나누면 예외가 발생하지 않으므로 의 값에 관계없이 0으로 나누기를 얻지 못합니다. 무한대를 반환합니다.
이제 a == b
true를 반환 하는 유일한 방법 은 정확히 동일한 비트를 포함 a
하고 b
포함하는 것입니다. 최하위 비트 만 다르면 그 차이는 0이 아닙니다.
편집하다 :
Bathsheba가 올바르게 언급했듯이 몇 가지 예외가 있습니다.
-
“숫자가 비교되지 않음”은 거짓이지만 비트 패턴은 동일합니다.
-
-0.0은 +0.0과 true를 비교하도록 정의되며 비트 패턴이 다릅니다.
모두 그래서 만약 a
하고 b
있다 Double.NaN
, 당신은 다른 절에 도달하지만, 이후 NaN - NaN
도 반환 NaN
, 당신은 0으로 나누어되지 않습니다.
답변
여기서 0으로 나누기가 발생할 수있는 경우는 없습니다.
SMT 해결사 Z3는 소수점 연산 부동 정확한 IEEE을 지원합니다. Z3에게 숫자 a
와 b
같은 것을 찾도록 요청합시다 a != b && (a - b) == 0
.
(set-info :status unknown)
(set-logic QF_FP)
(declare-fun b () (FloatingPoint 8 24))
(declare-fun a () (FloatingPoint 8 24))
(declare-fun rm () RoundingMode)
(assert
(and (not (fp.eq a b)) (fp.eq (fp.sub rm a b) +zero) true))
(check-sat)
결과는 UNSAT
입니다. 그런 숫자는 없습니다.
위의 SMTLIB 문자열은 또한 Z3가 임의의 반올림 모드 ( rm
) 를 선택할 수 있도록합니다 . 즉, 결과는 가능한 모든 반올림 모드 (5 개가 있음)에 적용됩니다. 그 결과 재생중인 변수가 NaN
무한대 일 수도 있습니다 .
a == b
로 구현됩니다 fp.eq
있도록 품질 +0f
과 -0f
동등 비교합니다. 0과의 비교도 사용 fp.eq
됩니다. 문제는 0으로 나누기를 피하는 것을 목표로하기 때문에 이것은 적절한 비교입니다.
평등 테스트는 비트 평등를 사용하여 구현 된, 경우 +0f
와 -0f
만드는 방법이었을 것 a - b
제로. 이 답변의 잘못된 이전 버전에는 궁금한 점에 대한 모드 세부 정보가 포함되어 있습니다.
Z3 온라인 은 아직 FPA 이론을 지원하지 않습니다. 이 결과는 최신 불안정한 분기를 사용하여 얻었습니다. 다음과 같이 .NET 바인딩을 사용하여 재생할 수 있습니다.
var fpSort = context.MkFPSort32();
var aExpr = (FPExpr)context.MkConst("a", fpSort);
var bExpr = (FPExpr)context.MkConst("b", fpSort);
var rmExpr = (FPRMExpr)context.MkConst("rm", context.MkFPRoundingModeSort());
var fpZero = context.MkFP(0f, fpSort);
var subExpr = context.MkFPSub(rmExpr, aExpr, bExpr);
var constraintExpr = context.MkAnd(
context.MkNot(context.MkFPEq(aExpr, bExpr)),
context.MkFPEq(subExpr, fpZero),
context.MkTrue()
);
var smtlibString = context.BenchmarkToSMTString(null, "QF_FP", null, null, new BoolExpr[0], constraintExpr);
var solver = context.MkSimpleSolver();
solver.Assert(constraintExpr);
var status = solver.Check();
Console.WriteLine(status);
(예 : 사례를 간과 어렵 기 때문에 IEEE 부동의 질문에 대답 Z3를 사용하는 것은 좋은 NaN
, -0f
, +-inf
) 당신이 임의의 질문을 할 수 있습니다. 사양을 해석하고 인용 할 필요가 없습니다. “이 특정 int log2(float)
알고리즘이 맞습니까?” 와 같은 혼합 부동 소수점 및 정수 질문을 할 수도 있습니다 .
답변
제공된 함수는 실제로 무한대를 반환 할 수 있습니다.
public class Test {
public static double calculation(double a, double b)
{
if (a == b)
{
return 0;
}
else
{
return 2 / (a - b);
}
}
/**
* @param args
*/
public static void main(String[] args) {
double d1 = Double.MIN_VALUE;
double d2 = 2.0 * Double.MIN_VALUE;
System.out.println("Result: " + calculation(d1, d2));
}
}
출력은 Result: -Infinity
입니다.
나누기의 결과가 두 배로 커질 때 분모가 0이 아닌 경우에도 무한대가 반환됩니다.
답변
IEEE-754를 따르는 부동 소수점 구현에서 각 부동 소수점 유형은 두 가지 형식으로 숫자를 보유 할 수 있습니다. 대부분의 부동 소수점 값에는 하나 ( “정규화 된”)가 사용되지만, 표시 할 수있는 두 번째로 작은 숫자는 가장 작은 것보다 약간 작기 때문에 그 차이는 동일한 형식으로 표현할 수 없습니다. 다른 ( “비정규 화 된”) 형식은 첫 번째 형식으로 표현할 수없는 매우 작은 숫자에만 사용됩니다.
비정규 화 된 부동 소수점 형식을 효율적으로 처리하는 회로는 비싸며 모든 프로세서에 포함되지는 않습니다. 일부 프로세서는 실제로 작은 숫자 에 대한 연산이 다른 값에 대한 연산보다 훨씬 느리거나 정규화 형식에 비해 너무 작은 숫자를 0으로 간주하는 것 중에서 선택할 수 있습니다.
Java 스펙은 구현이 비정규 화 된 형식을 지원해야 함을 나타내며, 그렇게하면 코드 실행 속도가 느려질 수 있습니다. 반면에 일부 구현에서는 값이 너무 작아서 값이 너무 작은 경우 값이 약간 느슨하게 처리되는 대신 약간 느슨하게 값을 처리하는 대신 코드를 더 빠르게 실행할 수있는 옵션을 제공 할 수 있습니다. 계산이 중요한 계산보다 10 배나 오래 걸리므로 성 가실 수 있으므로 많은 실제 상황에서 0으로 플러시는 느리지 만 정확한 산술보다 유용합니다.
답변
IEEE 754 이전의 예전에는 a! = b가 ab! = 0을 의미하지 않았으며 그 반대도 가능했습니다. 이것이 처음에 IEEE 754를 만드는 이유 중 하나였습니다.
IEEE 754에서는 거의 보장됩니다. C 또는 C ++ 컴파일러는 필요한 것보다 높은 정밀도로 작업을 수행 할 수 있습니다. 따라서 a와 b가 변수가 아니라 표현식 인 경우, a + b는 더 높은 정밀도로 한 번 계산 될 수 있기 때문에 (a + b)! = c는 (a + b)-c! = 0을 의미하지 않습니다. 더 높은 정밀도.
많은 FPU는 비정규 화 된 숫자를 반환하지 않고 0으로 대체하는 모드로 전환 할 수 있습니다.이 모드에서 a와 b가 작은 정규화 된 숫자 인 경우 차이가 가장 작은 정규화 된 숫자보다 작지만 0보다 큰 경우 a ! = b 또한 a == b를 보장하지 않습니다.
“부동 소수점 수를 절대로 비교하지 마십시오”는화물 컬트 프로그래밍입니다. “엡실론이 필요하다”라는 만트라를 가진 사람들 중에서 대부분은 엡실론을 올바르게 선택하는 방법을 모른다.