가장 작은 증분 (또는 그에 가깝게)으로 플로트를 변경하는 방법?
-
03-07-2019 - |
문제
나는있다 double
값 f
그리고 원본에 최대한 가깝지만 원본보다 엄격하게 더 큰 새로운 값을 얻기 위해 매우 크게 (또는 작은) 새 가치를 얻는 방법을 원합니다.
마지막 비트에 가까울 필요는 없습니다. 제가 변경하는 모든 것이 다른 값을 생성하고 원본으로 돌아 가지 않도록 보장하는 것이 더 중요합니다.
해결책
Math.h 파일을 확인하십시오. 운이 좋으면 nextafter
그리고 nextafterf
정의 된 기능. 그들은 휴대용 및 플랫폼 독립적 인 방식으로 원하는 것을 정확하게 수행하며 C99 표준의 일부입니다.
이를 수행하는 또 다른 방법 (폴백 솔루션이 될 수 있음)은 플로트를 Mantissa와 지수 부분으로 분해하는 것입니다. 증분은 쉽습니다. Mantissa에 하나를 추가하십시오. 오버플로가 발생하면 지수를 증가 시켜이 작업을 처리해야합니다. 감소는 같은 방식으로 작동합니다.
편집하다: 주석에서 지적했듯이 이진 표현에서 플로트를 증가시키는 것으로 충분합니다. Mantissa-Overflow는 지수를 증가시킬 것이며 이것이 바로 우리가 원하는 것입니다.
그것은 NextAfter가하는 것과 똑같은 일입니다.
이것은 완전히 휴대용이 아닙니다. 당신은 endianess를 다루어야하며 모든 기계에 IEEE 플로트가있는 것은 아니라는 사실을 다루어야합니다 (OK- 마지막 이유는 더 학문적입니다).
또한 Nan과 Infinites를 처리하는 것은 약간 까다로울 수 있습니다. 숫자가 아닌 정의에 따라 단순히 증가시킬 수는 없습니다.
다른 팁
u64 &x = *(u64*)(&f);
x++;
예, 진지하게.
편집하다: 누군가가 지적했듯이, 이것은 -Ve 숫자, INF, NAN 또는 오버플로를 제대로 다루지 않습니다. 위의 더 안전한 버전은입니다
u64 &x = *(u64*)(&f);
if( ((x>>52) & 2047) != 2047 ) //if exponent is all 1's then f is a nan or inf.
{
x += f>0 ? 1 : -1;
}
나는 똑같은 일을해야 했고이 코드를 생각해 냈습니다.
double DoubleIncrement(double value)
{
int exponent;
double mantissa = frexp(value, &exponent);
if(mantissa == 0)
return DBL_MIN;
mantissa += DBL_EPSILON/2.0f;
value = ldexp(mantissa, exponent);
return value;
}
가치가있는 것에 대해, 표준 ++ 증분 기능이 중단되는 가치는 9,007,199,254,740,992입니다.
이것은 정확히 당신이 원하는 것이 아닐 수도 있지만 여전히 찾을 수 있습니다. Numeric_Limits 사용 중. 특히 멤버 Min () 및 Epsilon ().
MyDouble이 이미 Epsilon에 가까워지지 않는 한 MyDouble + Numeric_limits :: Epsilon ()과 같은 것이 당신이 원하는 것을 할 것이라고 생각하지 않습니다. 그렇다면 운이 좋다.
나는이 코드를 잠시 전에 발견했다. 아마도 그것은 당신이 그것을 푸시 할 수있는 가장 작은 것을 결정하는 데 도움이 될 것이다. 불행히도 나는이 코드에 대한 참조를 기억할 수 없습니다.
#include <stdio.h>
int main()
{
/* two numbers to work with */
double number1, number2; // result of calculation
double result;
int counter; // loop counter and accuracy check
number1 = 1.0;
number2 = 1.0;
counter = 0;
while (number1 + number2 != number1) {
++counter;
number2 = number2 / 10;
}
printf("%2d digits accuracy in calculations\n", counter);
number2 = 1.0;
counter = 0;
while (1) {
result = number1 + number2;
if (result == number1)
break;
++counter;
number2 = number2 / 10.0;
}
printf("%2d digits accuracy in storage\n", counter );
return (0);
}