2009-03-13 3 views
3

을 절단 할 때 소수점 반올림 부동 : 나는 [min, max]의 범위에서 임의의 부동 소수점 값을 생성하는 함수를 작성하는 것을 시도하고있다이 아마 86 FPU 전문가에 대한 질문입니다

. 문제는 제 생성기 알고리즘 (부동 소수점 Mersenne Twister, 호기심이 있다면) 만 범위 [1,2]의 값을 반환한다는 것입니다. 즉, 상한선을 포함하지만 내 "소스"생성 값은 배타적 인 상한으로부터 여기서 catch는 기본 생성자가 8 바이트 double을 반환하지만 4 바이트 float 만 필요하며 가장 가까운 FPU 반올림 모드를 사용하고 있습니다.

내가 알고 싶은 것은이 경우 자르기 자체가 FPU 내부 80 비트 값이 충분히 가까운 경우 반환 값이 최대 값을 포함하는지 여부 또는 최대 값의 significand를 증가시켜야하는지 여부입니다. [1,2]에서 중개 무작위로 곱하기 전에, 또는 FPU 모드를 변경해야하는지). 물론 다른 아이디어도 있습니다.

는 여기에 내가 현재 사용하고있는 코드, 그리고 그 1.0F는 0x3f800000로 확인 확인 않았다

float MersenneFloat(float min, float max) 
{ 
    //genrand returns a double in [1,2) 
    const float random = (float)genrand_close1_open2(); 
    //return in desired range 
    return min + (random - 1.0f) * (max - min); 
} 

이 차이를 만드는 경우,이 Win32에서 MSVC++ 및 Linux GCC 모두에서 작동합니다. 또한 SSE 최적화의 모든 버전을 사용하면 이에 대한 대답이 변경됩니까?

편집 : 대답은 예입니다.이 경우에는 double에서 float로 잘라내어 결과가 최대 값을 포함하기에 충분합니다. 자세한 내용은 Crashworks의 답변을 참조하십시오.

답변

4

남남동 작전은 미묘하게이 알고리즘의 동작을 변경합니다. 좋은 소식은 쉽게 테스트하고 MSVC에/ARCH : SSE2 명령 줄 옵션을 지정하여 결과가 변경되는지 확인하면 일반 부동 소수점에 대해 x87 FPU 명령어 대신 SSE 스칼라 연산을 사용하게됩니다 수학.

정수 경계 주위에 정확한 반올림 동작이 무엇인지 잘 모르겠지만 테스트 할 때 1.999 ... 원래 포스터는이 테스트를 실행하고 절단과 함께 1.99999 함께 및/아치없이 모두 2로 반올림 것을 발견 : SSE2 예를 들어

static uint64 OnePointNineRepeating = 0x3FF FFFFF FFFF FFFF // exponent 0 (biased to 1023), all 1 bits in mantissa 
double asDouble = *(double *)(&OnePointNineRepeating); 
float asFloat = asDouble; 
return asFloat; 

편집, 결과에 의해 64 ~ 32 비트에서 반올림됩니다 .

+0

지금 내가 왜 다른 사람 사이에서 테스트를 실행하지 않았는지 :) 잘라내기를 사용하면 1.99999가/arch : SSE2가있는 경우와없는 경우 모두 2로 반올림된다는 것을 발견했습니다. 감사! –

+0

도와 줘서 기쁩니다 - 테스트의 결과가 나 자신인지 궁금했습니다. – Crashworks

0

범위의 양쪽 끝을 포함하도록 반올림을 조정하면 극단적 인 값이 극단적이지 않은 값의 절반도되지 않을까요?

+0

필자가 잘라내기를 사용하면 대답은 '예'이지만 최대 significand를 증가 시키면 대답은 '아니오'가됩니다. –

0

잘라내기를 사용하면 절대로 최대 값을 포함하지 않을 것입니다.

정말 최대 용량이 필요합니까? 말 그대로 거의 최대로 착륙 할 기회가 거의 없습니다. 지금, 당신이 전화를 할 때마다 genrand 여러 통화의 약간의 기회를 가지고

float MersenneFloat(float min, float max) 
{ 
    double random = 100000.0; // just a dummy value 
    while ((float)random > 65535.0) 
    { 
     //genrand returns a double in [1,2) 
     double random = genrand_close1_open2() - 1.0; // now it's [0,1) 
     random *= 65536.0; // now it's [0,65536). We try again if it's > 65535.0 
    } 
    //return in desired range 
    return min + float(random/65535.0) * (max - min); 
} 

참고 :

당신이 정밀도를 포기하고 있다는 사실을 악용 같은 것을 할 수 말했다 메르 센. 따라서 닫힌 간격에 대해 가능한 성능을 포기했습니다. 당신이 두 배에서 부동으로 추락하고 있기 때문에, 당신은 정밀도를 희생하지 않게됩니다.

편집 : - 수학이 정말 32 또는 64 비트에서 이루어집니다 그들은 중간 80 비트 표현이 없기 때문에 개선 된 알고리즘

+0

예, 최대 포함 (라이브러리 함수 계약)이 필요합니다. 곱셈을하기 전에 최대 값의 significand를 증가시키는 것과는 대조적으로 자신의 방식대로하는 것이 어떤 이점이 있습니까? –

+0

그럴 수도 있습니다. 그러나 어딘가에는 거절 테스트를해야하거나 완벽하지 않은 가치관을 가지고 있습니다. 이 문제의 유사점은 말하자면, 무작위 int 0-65535에서 정수 0-256을 생성한다는 것입니다. 그것은 단지 고르게 매핑하지 않습니다. – rlbond

+0

사실, 방금 Crashworks 테스트 제안을 시도했지만 잘라내 기는 실제로 반올림합니다. –