2014-09-15 5 views
16

float (a.k.a. 싱글) 값은 4 바이트 값이며 임의의 실수 값을 나타냅니다. 형식이 지정되고 바이트 수는 제한되어 있으므로 표현할 수있는 최소값과 최대 값이 있으며 자체 값에 따라 한정된 정밀도를 갖습니다.부동 소수점과 가장 가까운 등가 값을 어떻게 찾을 수 있습니까?

플로트의 유한 정밀도를 고려할 때 가능한 가장 가까운 값을 기준값보다 높거나 낮출 수있는 방법이 있는지 알고 싶습니다. 정수를 사용하면 간단합니다. 즉, 1을 더하거나 뺍니다. 그러나 float을 사용하면 최소 float 값을 더하거나 뺄 수없고 원래 값과 다를 수 있습니다. 나는.

float FindNearestSmaller (const float a) 
{ 
    return a - FLT_MIN; /* This doesn't necessarily work */ 
} 

실제로 위의 내용은 거의 작동하지 않습니다. 위의 경우에 FLT_MINa의 정밀도를 훨씬 웃도므로 반환 값은 일반적으로 여전히 a입니다. 직접 시도해 볼 수 있습니다. 예 : 0.0f, 또는 주문 FLT_MIN의 아주 작은 숫자에 대한 아니라 0과 100

사이에 아무것도 그래서 당신은 부동 소수점 정밀도 주어진 가장 가까운하지만 작거나 a보다 큰 값을 얻을 얼마나?

참고 : 주로 C/C++ 응답에 관심이 있지만 대부분의 프로그래밍 언어에 대한 대답이 해당됩니다.

+0

정말 궁금한 답변입니다. – Almo

+0

'memcpy'를 정수로 변환하고,'memcpy'를'float'에 다시 넣으시겠습니까? –

+0

@BartFriederichs : 아니요, 모든 경우에 작동하지 않습니다. –

답변

13

부동 소수점 값의 이웃을 찾는 표준 방법은 double의 경우 nextafter이고 float의 경우 nextafterf의 경우입니다. 두 번째 인수는 방향을 제공합니다. 무한 성은 IEEE 754 부동 소수점의 합법적 인 값이므로 nextafter(x, +1.0/0.0)을 호출하면 x 바로 위의 값을 얻을 수 있으며 DBL_MAX 일 수도 있습니다 (반면에 nextafter(x, DBL_MAX)을 작성하면 x == DBL_MAX을 적용하면 DBL_MAX이 반환됩니다).). 때때로 유용

두 표준이 아닌 방법은 다음과 같습니다

  1. 액세스 부호 같은 크기의 정수 및 증가 또는 감소이 정수로 float/double의 표현입니다. 부동 소수점 형식은 양수 부동 소수점 및 음수 부동 소수점 각각에 대해 정수로 간주되는 표현의 비트가 표현 된 부동 소수와 함께 단조롭게 전개되도록 신중하게 설계되었습니다.

  2. 반올림 모드를 위로으로 변경하고 최소 양의 부동 소수점 숫자를 추가하십시오. 가장 작은 양의 부동 소수점 숫자는 두 개의 부동 소수점 사이에있을 수있는 최소 증분이기 때문에 부동 소수점을 건너 뛰지 않습니다. 가장 작은 양의 부동 소수점 숫자는 FLT_MIN * FLT_EPSILON입니다.완성도를 위해서


, 난에서 반올림 모드를 변경하지 않고이 추가됩니다 자사의 "가장 가까운"기본적으로, (1.0f + FLT_EPSILON)에 의해 float를 곱하면 거리 중 하나 바로 이웃에서 숫자를 생산 제로, 또는 그 이후의 이웃. 아마도 당신이 증가/감소시키고 자하는 플로트의 부호를 이미 알고 있다면 그것은 아마도 가장 값이 쌉니다. 그리고 때때로 그것은 이웃을 생성하지 않는다고 생각하지 않습니다. nextafternextafterf은 x86상의 correct implementation이 많은 특수 값과 FPU 상태를 테스트해야하며, 따라서 그 값이 다소 비쌉니다.

0을 향해 이동하려면 1.0f - FLT_EPSILON을 곱하십시오.

이것은 분명히 0.0f에서는 작동하지 않으며 일반적으로 더 작은 비정규 화 된 숫자에 대해서는 작동하지 않습니다.

숫자가 2 ulps는 기초적으로 1.0f + FLT_EPSILON 미리 곱하는하는 구체적 구간 [0.75 * 2 ... P 2 P)에서 단지 2의 제곱 미만이다. 곱셈과 덧셈을 신경 쓸 필요가 없다면, x + (x * (FLT_EPSILON * 0.74))은 모든 일반 숫자에 대해 작동해야합니다 (그러나 여전히 0이나 모든 작은 비정규 숫자에 대해서는 작동하지 않습니다).

11

표준 C (및 아마 C++, 그러나 나는 검사하지 않았다)의 일부인 "nextafter"함수를 보라.

+0

http://linux.die.net/man/3/nextafter –

+1

C++ 11에서 시작하는 std :: nextafter 및 std :: nexttoward가 있습니다. –

0

나는 내 컴퓨터에서 그것을 시도했다. 그리고 세 가지 접근 방법 :
1. 1 추가하고
가 같은 대답을 줄 것으로 보인다 (1.0F + FLT_EPSILON)에 의해 FLT_EPSILON에게
3. 곱셈을 추가
2. memcopying.



여기에 결과를 볼

의 bash-3.2 $ CC float_test.c -o float_test; ./float_test 1.023456 10
원래 NUM : 1.023456
INT는 = 추가 1.023456 01-EPS 첨가 = 1.023456 MULT 01 * (EPS + 1) = 의해 1.023456
INT는 = 추가 1.023456 02 EPS 첨가 = 1.023456 MULT 02로 * (eps + 1) = 1.023456
int = 1.023456 03-eps added = 1.023456x03 * (eps + 1) = 1.023456
int added = 1.023456 04-eps added = 1.023456x4 by (eps + 1)) = 1.023456
INT 첨가 = 1.023457 05 EPS 05 * (EPS + 1) = 의해 = 1.023457 MULT 추가 1.023457
INT는 = 추가 1.023457 06-EPS 첨가 = 1.023457 MULT 06 * (EPS + 1) = 1.023457
하여 (eps + 1) = 1.023457 012-009457x = 1.023457
int added = 1.023457 08-eps added = 1.023457 mult by 08 * (eps + 1) = 1.023457
int added = 1.023457 09-EPS 09 * (EPS + 1) = 의해 = 1.023457 MULT 추가 1.023457
INT는 = 추가 1.023457 10 EPS 첨가 = 1.023457 MULT 10 * (EPS + 1) = 1.023457

코드

하여
#include <float.h> 
#include <stdlib.h> 
#include <stdio.h> 
#include <string.h> 
#include <assert.h> 

int main(int argc, char *argv[]) 
{ 

    if(argc != 3) { 
     printf("Usage: <binary> <floating_pt_num> <num_iter>\n"); 
     exit(0); 
    } 

    float f = atof(argv[1]); 
    int count = atoi(argv[2]); 

    assert(count > 0); 

    int i; 
    int num; 
    float num_float; 

    printf("Original num: %f\n", f); 
    for(i=1; i<=count; i++) { 
     memcpy(&num, &f, 4); 
     num += i; 
     memcpy(&num_float, &num, 4); 
     printf("int added = %f \t%02d-eps added = %f \tmult by %2d*(eps+1) = %f\n", num_float, i, f + i*FLT_EPSILON, i, f*(1.0f + i*FLT_EPSILON)); 
    } 

    return 0; 
} 
+1

'% .9f'는 무슨 일이 일어나고 있는지 보여주기 위해'% f'보다 낫고,'% a'는 더 좋습니다. –

+0

-1023.9와 -1024.1과 같이 2의 거듭 제곱의 한쪽에만 (그리고 1.0에서 어쩌면'-'와 떨어져서) 숫자에 대한 테스트를 제안하십시오. 확신할만한 중요한 차이점이 표시됩니다. – chux

+0

@chux 잘 예상됩니다. '1 + FLT_EPSILON'을 곱한 큰 숫자는 더 큰 드리프트를 줄 것입니다. 또한 add-by-1 정수 방법은 더 많은 양의 가수에 영향을 미칩니다. 어떤 경우에는 '2^30'과 같이 지수도 바뀝니다. 그러나'FLT_EPSILON'에 의한 추가는 여전히 일관된 결과를 만들어냅니다. 사실 우리가 더 큰 숫자를 변경하기 때문에 변화는 더 느립니다. 작은'FLT_EPSILON'의 효과는 음영 처리됩니다. –