엡실론을 사용하여 double을 0으로 비교


214

오늘은 C ++ 코드 (다른 사람이 작성)를 살펴 보고이 섹션을 찾았습니다.

double someValue = ...
if (someValue <  std::numeric_limits<double>::epsilon() && 
    someValue > -std::numeric_limits<double>::epsilon()) {
  someValue = 0.0;
}

이것이 의미가 있는지 알아 내려고 노력 중입니다.

에 대한 설명서 epsilon()는 다음과 같습니다.

이 함수는 1과 1보다 큰 가장 작은 값의 차이 (더블로 표시)를 반환합니다.

이것은 0에도 적용됩니까, 즉 epsilon()가장 작은 값이 0보다 큽니까? 또는 사이이 번호는 00 + epsilon그가에 의해 표현 될 수있다 double?

그렇지 않으면 비교가 someValue == 0.0?에 해당하지 않습니까?


3
1 주위의 엡실론은 0 주위의 값보다 훨씬 높을 것이므로 0과 0 + epsilon_at_1 사이의 값이있을 것입니다. 이 섹션의 저자는 작은 것을 사용하고 싶었지만 마술 상수를 사용하고 싶지 않았기 때문에 본질적으로 임의의 값을 사용했습니다.
enobayram

2
부동 소수점 수를 비교하는 것은 어렵고 엡실론 또는 임계 값의 사용도 권장됩니다. 를 참조하십시오 : cs.princeton.edu/introcs/91floatcygnus-software.com/papers/comparingfloats/comparingfloats.htm
디트 야 쿠마 펜디 교수

40
첫 번째 링크는 403.99999999입니다
graham.reeds

6
이 경우 IMO의 사용 numeric_limits<>::epsilon은 오도의 소지가 있고 관련이 없습니다. 우리가 원하는 것은 실제 값이 0에서 ε 이하로 차이가 나는 경우 0으로 가정하는 것입니다. 그리고 ε은 기계 종속 값이 아니라 문제 사양에 따라 선택해야합니다. 현재 엡실론은 쓸모가 없다고 생각합니다. 몇 번의 FP 작업만으로도 그보다 큰 오류가 누적 될 수 있습니다.
Andrey Vihrov

1
+1. 엡실론은 가능한 가장 작은 것이 아니지만 필요한 정밀성과 수행중인 작업을 알면 대부분의 실제 엔지니어링 작업에서 주어진 목적을 달성 할 수 있습니다.
SChepurin

답변:


192

64 비트 IEEE double을 가정하면 52 비트 가수와 11 비트 지수가 있습니다. 비트로 나누자 :

1.0000 00000000 00000000 00000000 00000000 00000000 00000000 × 2^0 = 1

1보다 큰 가장 작은 표현 가능 숫자

1.0000 00000000 00000000 00000000 00000000 00000000 00000001 × 2^0 = 1 + 2^-52

따라서:

epsilon = (1 + 2^-52) - 1 = 2^-52

0에서 엡실론 사이의 숫자가 있습니까? Plenty ... 예를 들어 최소 양수 (정상) 숫자는 다음과 같습니다.

1.0000 00000000 00000000 00000000 00000000 00000000 00000000 × 2^-1022 = 2^-1022

실제로 (1022 - 52 + 1)×2^52 = 43729952381767516160과 엡실론 사이의 숫자 가 있으며 이는 모든 양의 표현 가능한 숫자의 47 %입니다 ...


27
"정수의 47 %"라고 말할 수있는 것이 이상합니다. :)
구성자

13
@configurator : 아냐, 말할 수는 없습니다 ( '자연적인'유한 한 측정법은 존재하지 않습니다). 그러나 "양수로 표현할 수있는 숫자 의 47 %"라고 말할 수 있습니다 .
Yakov Galka

1
@ybungalobill 알아낼 수 없습니다. 지수에는 11 비트가 있습니다 : 1 부호 비트와 10 값 비트. 왜 2 ^ -1024가 아닌 2 ^ -1022가 가장 작은 양수입니까?
Pavlo Dyban

3
@PavloDyban : 지수가 간단하기 때문에 하지 않는 부호 비트가 있습니다. 그것들은 오프셋으로 인코딩됩니다 : 만약 인코딩 된 지수 0 <= e < 2048라면, 가수는의 거듭 제곱에 2를 곱합니다 e - 1023. 예를 들어의 지수는 , as 및 as 로 2^0인코딩 됩니다 . 의 값은 비정규 및 실수 0을 위해 예약되어 있습니다. e=10232^1e=10242^-1022e=1e=0
Yakov Galka

2
@PavloDyban : 또한 2^-1022가장 작은 정규 숫자입니다. 가장 작은 숫자는 실제로 0.0000 00000000 00000000 00000000 00000000 00000000 00000001 × 2^-1022 = 2^-1074입니다. 이는 비정상이어서 가수 부분이 1보다 작으므로 지수로 인코딩됩니다 e=0.
Yakov Galka

17

테스트는 확실히 동일하지 않습니다 someValue == 0. 부동 소수점 숫자의 전체 아이디어는 지수와 의미를 저장한다는 것입니다. 따라서 특정 수의 이진 유효 정밀도 수치 (IEEE double의 경우 53)로 값을 나타냅니다. 표현 가능한 값은 1에 비해 0에 훨씬 더 밀집되어 있습니다.

보다 친숙한 10 진수 시스템을 사용하려면 지수와 함께 "4 자리 유효 숫자까지"10 진수 값을 저장한다고 가정하십시오. 그런 다음 표현할 수있는 값보다 크 1입니다 1.001 * 10^0, 그리고 epsilon이다 1.000 * 10^-3. 그러나 1.000 * 10^-4지수가 -4를 저장할 수 있다고 가정하면 표현할 수 있습니다. IEEE double 은의 지수보다 적은 지수 저장할 수 있다는 내 말을들 있습니다 epsilon.

이 코드만으로는 epsilon구체적으로 바운드 로 사용하는 것이 타당한 지 여부를 알 수 없으므로 컨텍스트를 살펴 봐야합니다. 그것은 그 수있다 epsilon생성 된 계산의 오차의 적절한 추정치 someValue, 그리고 그렇지 않은 것일 수있다.


2
좋은 지적이지만, 그 경우에도 오류를 합리적인 이름의 변수에 묶고 비교에 사용하는 것이 좋습니다. 그대로, 마법 상수와 다르지 않습니다.
enobayram

아마도 내 질문에서 더 명확 해 졌을 것입니다. epsilon이 계산 오류를 다루기에 충분히 큰 "임계 값"인지 여부에 대해서는 의문을 제기하지 않았지만이 비교가 같은지 아닌지는 의심 someValue == 0.0하지 않았습니다.
세바스찬 크리스 만 스키

13

epsilon은 1과 1보다 위에 표시 될 수있는 다음으로 가장 높은 숫자의 차이이며 0과 0보다 높게 표시 될 수있는 다음으로 가장 높은 숫자와의 차이가 아니기 때문에 0과 엡실론 사이에 존재하는 숫자가 있습니다. 코드는 거의 수행하지 않습니다) :-

#include <limits>

int main ()
{
  struct Doubles
  {
      double one;
      double epsilon;
      double half_epsilon;
  } values;

  values.one = 1.0;
  values.epsilon = std::numeric_limits<double>::epsilon();
  values.half_epsilon = values.epsilon / 2.0;
}

디버거를 사용하여 main의 끝에서 프로그램을 중지하고 결과를 보면 epsilon / 2가 epsilon, 0 및 1과 다르다는 것을 알 수 있습니다.

따라서이 함수는 +/- epsilon 사이의 값을 취하여 0으로 만듭니다.


5

다음 프로그램을 사용하여 숫자 (1.0, 0.0, ...) 주위의 엡실론 (가장 작은 차이)의 근사값을 인쇄 할 수 있습니다. 다음과 같은 결과를 출력합니다.
epsilon for 0.0 is 4.940656e-324
epsilon for 1.0 is 2.220446e-16
약간 생각하면 지수가 해당 숫자의 크기에 맞게 조정할 수 있기 때문에 엡실론 값을 보는 데 사용하는 숫자가 엡실론의 크기가 작을수록 작아집니다.

#include <stdio.h>
#include <assert.h>
double getEps (double m) {
  double approx=1.0;
  double lastApprox=0.0;
  while (m+approx!=m) {
    lastApprox=approx;
    approx/=2.0;
  }
  assert (lastApprox!=0);
  return lastApprox;
}
int main () {
  printf ("epsilon for 0.0 is %e\n", getEps (0.0));
  printf ("epsilon for 1.0 is %e\n", getEps (1.0));
  return 0;
}

2
어떤 구현을 확인 했습니까? GCC 4.7의 경우에는 그렇지 않습니다.
Anton Golov

3

16 비트 레지스터에 맞는 장난감 부동 소수점 숫자로 작업한다고 가정합니다. 부호 비트, 5 비트 지수 및 10 비트 가수가 있습니다.

이 부동 소수점 숫자의 값은 가수이며 2 진 10 진수 값으로 해석되며 지수의 거듭 제곱에 2를 곱한 값입니다.

1 주위의 지수는 0과 같습니다. 가수의 가장 작은 숫자는 1024의 일부입니다.

지수의 1/2에 가까운 지수는 -1이므로 가수의 가장 작은 부분은 절반입니다. 5 비트 지수를 사용하면 음수 16에 도달 할 수 있으며, 가수의 가장 작은 부분은 32m에서 한 부분의 가치가 있습니다. 그리고 음수 16 지수에서 값은 32k에서 한 부분 정도이며 위에서 계산 한 엡실론보다 0에 훨씬 가깝습니다!

이제 이것은 실제 부동 소수점 시스템의 모든 단점을 반영하지 않는 장난감 부동 소수점 모델이지만 엡실론보다 작은 값을 반영하는 기능은 실제 부동 소수점 값과 상당히 유사합니다.


3

차이 X및 다음의 값 X에 따라 변화 X. 의 다음 값과
epsilon()의 차이점 1입니다 1. 와 다음 값의
차이 는 없습니다 .00epsilon()

대신 다음 std::nextafter0같이 double 값을 비교하는 데 사용할 수 있습니다 .

bool same(double a, double b)
{
  return std::nextafter(a, std::numeric_limits<double>::lowest()) <= b
    && std::nextafter(a, std::numeric_limits<double>::max()) >= b;
}

double someValue = ...
if (same (someValue, 0.0)) {
  someValue = 0.0;
}

2

컴퓨터 의 정확성 에 달려 있다고 생각 합니다. 이 표를 살펴보십시오 : 엡실론이 두 배로 표시되지만 정밀도가 높으면 비교가

someValue == 0.0

어쨌든 좋은 질문입니다!


2

가수와 지수로 인해 0에 적용 할 수 없습니다. 지수로 인해 엡실론보다 작은 매우 작은 숫자를 저장할 수 있지만 (1.0- "매우 작은 숫자")와 같은 것을 시도하면 1.0이됩니다. 엡실론은 가치가 아니라 가치 정밀도의 지표입니다. 저장할 수있는 올바른 10 진수 숫자를 표시합니다.


2

0이 아닌 가장 작은 양수 값과 0이 아닌 가장 작은 음수 값 사이에 IEEE 부동 소수점을 사용하면 양수 0과 음수 0의 두 값이 있습니다. 값이 0이 아닌 가장 작은 값 사이에 있는지 테스트하는 것은 0과 같은지 테스트하는 것과 같습니다. 그러나 할당은 음의 0을 양의 0으로 변경하기 때문에 효과가있을 수 있습니다.

부동 소수점 형식은 최소 유한 양수와 음수 값 사이에서 양의 무한대, 부호없는 0 및 음의 무한대의 세 가지 값을 가질 수 있습니다. 나는 실제로 그 방식으로 작동하는 부동 소수점 형식에 익숙하지 않지만 그러한 동작은 IEEE보다 완벽하게 합리적이고 틀림없이 더 좋을 것입니다 (아마도 하드웨어를 추가 할 가치가 있지만 수학적으로는 충분하지 않을 것입니다. 1 / (1 / INF), 1 / (-1 / INF) 및 1 / (1-1)은 서로 다른 세 개의 0을 나타내는 세 가지 경우를 나타냅니다. 나는 C 표준이 부호있는 무한 기호가 존재한다면, 0과 같아야한다고 명령하는지 여부를 모른다. 그렇지 않은 경우 위와 같은 코드는 예를 들어


"1 / (1-1)"(예제에서) 무한대가 아닌 0이 아닌가?
Sebastian Krysmanski

양 (1-1), (1 / INF) 및 (-1 / INF)는 모두 0을 나타내지 만 양수를 각각으로 나누면 이론적으로 세 가지 다른 결과를 산출해야합니다 (IEEE 수학은 처음 두 항목을 동일한 것으로 간주합니다) ).
supercat December

1

따라서 시스템이 1.000000000000000000000과 1.000000000000000000001을 구분할 수 없다고 가정 해 봅시다. 즉 1.0과 1.0 + 1e-20입니다. -1e-20과 + 1e-20 사이에 나타낼 수있는 값이 여전히 있다고 생각하십니까?


0을 제외하고는 -1e-20과 + 1e-20 사이의 값이 있다고 생각하지 않습니다. 그러나 이것이 사실이 아니라고 생각하기 때문입니다.
Sebastian Krysmanski

@SebastianKrysmanski : 사실이 아니며 0과 사이에 많은 부동 소수점 값이 epsilon있습니다. 고정 소수점이 아닌 부동 소수점 이기 때문 입니다.
Steve Jessop

0과 구별되는 가장 작은 표현 가능한 값은 지수를 나타 내기 위해 할당 된 비트 수에 의해 제한됩니다. 따라서 double에 11 비트 지수가 있으면 가장 작은 숫자는 1e-1023입니다.
cababunga

0

또한 좋은 이유 그러한 기능을 갖는 는 "비정상"(더 이상 묵시적 선행 "1"을 사용할 수없고 특별한 FP 표현을 가질 수없는 매우 작은 숫자)을 제거하는 것입니다. 왜 이렇게 하시겠습니까? 일부 기계 (특히 일부 구형 펜티엄 4)는 비정상 처리시 실제로 느려집니다. 다른 사람들은 다소 느려집니다. 응용 프로그램에 실제로 매우 작은 숫자가 필요하지 않은 경우 0으로 플러시하는 것이 좋습니다. IIR 필터 또는 감쇠 기능의 마지막 단계는이 점을 고려해야합니다.

참조 : 0.1f를 0으로 변경하면 성능이 10 배 저하되는 이유는 무엇입니까?

http://en.wikipedia.org/wiki/Denormal_number


1
이것은 비정규 화 된 숫자보다 더 많은 숫자를 제거합니다. Planck의 상수 또는 전자의 질량을 0으로 변경하여이 숫자를 사용하면 매우 잘못된 결과를 얻을 수 있습니다.
gnasher729
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.