我有一个double
值f
,并且希望有一种方法可以将它稍微大一点(或小一点)以获得一个新值,该值将尽可能接近原始值,但仍严格大于(或小于)原始值。
它不必关闭到最后一点——更重要的是,我所做的任何更改都可以保证产生不同的值,而不是返回到原来的值。
我有一个double
值f
,并且希望有一种方法可以将它稍微大一点(或小一点)以获得一个新值,该值将尽可能接近原始值,但仍严格大于(或小于)原始值。
它不必关闭到最后一点——更重要的是,我所做的任何更改都可以保证产生不同的值,而不是返回到原来的值。
检查你的 math.h 文件。如果你幸运的话,你已经定义了nextafter
andnextafterf
函数。它们以可移植且独立于平台的方式完全满足您的需求,并且是 C99 标准的一部分。
另一种方法(可能是后备解决方案)是将浮点数分解为尾数和指数部分。递增很简单:只需将尾数加一即可。如果你得到一个溢出,你必须通过增加你的指数来处理这个问题。递减的工作方式相同。
编辑:正如评论中所指出的,只需在其二进制表示中增加浮点数就足够了。尾数溢出将增加指数,这正是我们想要的。
简而言之,这与 nextafter 所做的相同。
不过,这不会是完全可移植的。您将不得不处理字节顺序以及并非所有机器都具有 IEEE 浮点数的事实(好的 - 最后一个原因更具学术性)。
处理 NAN 和无限可能有点棘手。您不能简单地增加它们,因为它们根据定义而不是数字。
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;
}
绝对而言,您可以添加到浮点值以生成新的不同值的最小量将取决于该值的当前大小;它将是类型的机器 epsilon乘以当前指数。
查看IEEE浮点表示规范。最简单的方法是将值重新解释为整数类型,加 1,然后通过检查符号位和指数位检查(如果您关心的话)您是否没有翻转符号或生成 NaN。
或者,您可以使用frexp获取当前尾数和指数,从而计算要添加的值。
我需要做同样的事情并想出了这段代码:
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);
}