다음 예제에서 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 부동 소수점 연산을 요구하기 때문입니다. 로부터 사양 :0a != 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 == btrue를 반환 하는 유일한 방법 은 정확히 동일한 비트를 포함 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를 보장하지 않습니다.
“부동 소수점 수를 절대로 비교하지 마십시오”는화물 컬트 프로그래밍입니다. “엡실론이 필요하다”라는 만트라를 가진 사람들 중에서 대부분은 엡실론을 올바르게 선택하는 방법을 모른다.
