题
我有一个 double
值 f
,想要一个稍微大一点(或更小)的方法来获得一个尽可能接近的新值到原件但仍然严格大于(或小于)原件。
它不必靠近最后一位—更重要的是,无论我做出什么改变,都能保证产生不同的值,而不是回到原来的位置。
解决方案
检查math.h文件。如果你很幸运,你有 nextafter
和 nextafterf
定义的功能。它们以便携式和平台无关的方式完成您想要的工作,并且是C99标准的一部分。
另一种方法(可能是后备解决方案)是将你的浮点数分解为尾数和指数部分。增量很简单:只需在尾数中添加一个即可。如果出现溢出,则必须通过递增指数来处理此问题。减少的方式也是一样的。
编辑:正如评论中指出的那样,只需在其二进制表示中增加浮点数就足够了。尾数溢出将增加指数,这正是我们想要的。
简而言之,这与nextafter相同。
但这不是完全可移植的。你不得不处理endianess以及并非所有机器都有IEEE浮动的事实(好的 - 最后一个原因是更具学术性)。
处理NAN和infinite也有点棘手。你不能简单地增加它们,因为它们是定义而不是数字。
其他提示
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 + numeric_limits :: epsilon()这样的东西会做你想要的,除非mydouble已经接近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);
}