1.0이 아닌 1.0에 가장 가까운 두 배는 얼마입니까?


88

1.0에 가장 가깝지만 실제로 1.0이 아닌 double을 프로그래밍 방식으로 얻을 수있는 방법이 있습니까?

이를 수행하는 한 가지 해키 방법은 double을 동일한 크기의 정수로 memcpy 한 다음 1을 빼는 것입니다. IEEE754 부동 소수점 형식이 작동하는 방식은 분수 부분을 모두 0 (1.000000000000)에서 모두 1 (1.111111111111)로 변경하면서 지수를 1만큼 감소시킵니다. 그러나 정수가 리틀 엔디안으로 저장되고 부동 소수점이 빅 엔디안으로 저장되는 기계가 있으므로 항상 작동하지는 않습니다.


4
+1이 -1과 같은 거리 (1.0에서)라고 가정 할 수 없습니다. 10 진법과 2 진법 부동 소수점 표현의 인터리빙은 간격이 고르지 않음을 의미합니다.
리처드 Critten

2
@Richard : 당신 말이 맞아요. 하나의 ULP를 빼는 것이 "nextbefore"값을 얻을 가능성은 매우 낮습니다. 왜냐하면 지수도 조정되어야하기 때문입니다. nextafter()그가 원하는 것을 달성하는 유일한 방법입니다.
Rudy Velthuis

1
참고로이 블로그 (안 내)의 읽고 : exploringbinary.com/...
리처드 Critten

1
@RudyVelthuis : 모든 IEEE754 바이너리 부동 소수점 형식에서 작동합니다.
Edgar Bonet

1
좋습니다. "모든 IEEE754 부동 소수점 형식에서 작동하는 것"은 무엇입니까? 만약 당신이 significand를 감소 시키면 "firstbefore ()"값을 얻는다는 것은 사실이 아닙니다. 특히 2의 거듭 제곱 인 significand를 가진 1.0의 경우가 아닙니다. 즉, 1.0000...이진수는 감소 0.111111....하고 정규화하려면 왼쪽으로 이동해야합니다 1.11111....이 경우 지수를 감소시켜야합니다. 그리고 1.0에서 2ulp 떨어져 있습니다. 그래서 아니요, 적분 값에서 1을 빼는 것은 여기서 요구되는 것을 제공하지 않습니다.
Rudy Velthuis

답변:


23

C 및 C ++에서 다음은 1.0에 가장 가까운 값을 제공합니다.

#include <limits.h>

double closest_to_1 = 1.0 - DBL_EPSILON/FLT_RADIX;

그러나 이후 버전의 C ++에서는 limits.h더 이상 사용되지 않고 climits. 그러나 어쨌든 C ++ 특정 코드를 사용하는 경우

#include <limits>

typedef std::numeric_limits<double> lim_dbl;
double closest_to_1 = 1.0 - lim_dbl::epsilon()/lim_dbl::radix;

그리고 Jarod42가 그의 대답에 쓴 것처럼 C99 또는 C ++ 11 이후로 다음을 사용할 수도 있습니다 nextafter.

#include <math.h>

double closest_to_1 = nextafter(1.0, 0.0);

물론 C ++에서는 대신 포함 cmath하고 사용할 수 있습니다 (나중 C ++ 버전의 경우) std::nextafter.


143

C ++ 11부터 nextafter주어진 방향으로 다음 표현 가능한 값을 얻기 위해 사용할 수 있습니다 .

std::nextafter(1., 0.); // 0.99999999999999989
std::nextafter(1., 2.); // 1.0000000000000002

데모


11
이것은 다음 표현 가능한 정수로 double을 증가시키는 좋은 방법이기도합니다 : std::ceil(std::nextafter(1., std::numeric_limits<double>::max())).
Johannes Schaub-litb

43
다음 질문은 "이것이 stdlib에서 어떻게 구현되는지"입니다. : P
Lightness Races in Orbit

17
@LightnessRacesinOrbit의 댓글을 읽은 후 궁금해졌습니다. 이것이 glibc nextafter가을 구현하는 방법이며 , 다른 사람이 어떻게 수행되는지보고 싶어 할 경우 musl이이를 구현하는 방법입니다. 기본적으로 : 원시 비트 트위들 링.
Cornstalks

2
@Cornstalks : 조금 뒤틀리는 것이 놀랍지 않습니다. 유일한 다른 옵션은 CPU를 지원하는 것입니다.
Matthieu M.

5
비트 트위들 링은 제대로 할 수있는 유일한 방법입니다, IMO. 천천히 접근하려고 시도하면서 많은 테스트 시도를 할 수 있지만 매우 느릴 수 있습니다.
Rudy Velthuis

25

C에서는 다음을 사용할 수 있습니다.

#include <float.h>
...
double value = 1.0+DBL_EPSILON;

DBL_EPSILON 1과 표현 가능한 1보다 큰 최소값의 차이입니다.

실제 값을 보려면 여러 자리로 인쇄해야합니다.

내 플랫폼에서 printf("%.16lf",1.0+DBL_EPSILON)제공합니다 1.0000000000000002.


10
이외에 다른 값이 작동 실 거예요 그래서 1.같은1'000'000 데모
Jarod42

7
@ Jarod42 : 당신 말이 맞지만 OP는 1.0. BTW, 또한 1보다 큰 가장 가까운 값을 제공하며 1에 절대적으로 가장 가까운 값 (1보다 작을 수 있음)이 아닙니다. 그래서 나는 이것이 부분적인 대답이라는 데 동의하지만 그럼에도 불구하고 기여할 수 있다고 생각했습니다.
barak manos

@ LưuVĩnhPhúc : 답의 제한에 대해 정확하고 다른 방향으로 가장 가까운 것을 알려줍니다.
Jarod42

7
이것은 1.0에 가장 가까운 두 배를 제공하지 않습니다. (밑수 2 가정) 1.0 바로 의 두 배 는 1.0 바로 의 두 배 (당신이 계산 한 것)의 절반 정도 떨어져 있기 때문 입니다.
celtschk

@celtschk : 당신이 맞아요, 위의 댓글에서 설명했습니다.
barak manos

4

C ++에서는 이것을 사용할 수도 있습니다.

1 + std::numeric_limits<double>::epsilon()

1
barak manos '답변과 마찬가지로 1 이외의 값에는 작동하지 않습니다.
zwol

2
@zwol은 일반적인 이진 부동 소수점 구현의 경우 기술적으로 1 ~ 2epsilon 사이의 모든 값에 대해 작동합니다. 하지만, 네, 1 번에만 지원하는 것이 보장되는 것이
맞습니다

7
기술적으로는 1에 대해 작동하지 않습니다. 1에 가장 가까운 숫자는 바로 다음 숫자가 아니라 1 바로 앞의 숫자이기 때문입니다. 0.5와 1 사이의 double 의 정밀도는 1과 2 사이의 정밀도보다 두 배 높으므로 1 바로 앞의 숫자는 1에 가까워집니다.
HelloGoodbye
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.