C++ 当我使用C++;带整数的数学函数?

C++ 当我使用C++;带整数的数学函数?,c++,algorithm,math,floating-point,floating-accuracy,C++,Algorithm,Math,Floating Point,Floating Accuracy,例如,由于浮点数的精度,下面的代码将给出不理想的结果 double a = 1 / 3.0; int b = a * 3; // b will be 0 here 我想知道如果我使用数学函数,是否会出现类似的问题。比如说 int a = sqrt(4); // Do I have guarantee that I will always get 2 here? int b = log2(8); // Do I have guarantee that I wil

例如,由于浮点数的精度,下面的代码将给出不理想的结果

double a = 1 / 3.0;
int b = a * 3;      // b will be 0 here
我想知道如果我使用数学函数,是否会出现类似的问题。比如说

int a = sqrt(4);       // Do I have guarantee that I will always get 2 here?
int b = log2(8);       // Do I have guarantee that I will always get 3 here?
如果没有,如何解决这个问题

编辑:

实际上,我在为算法任务编程时遇到了这个问题。我想去那里

最大整数,为2的幂,小于或等于整数N

所以圆函数不能解决我的问题。我知道我可以通过一个循环来解决这个问题,但它似乎不是很优雅

我想知道

int a = pow(2, static_cast<int>(log2(N)));
inta=pow(2,静态_-cast(log2(N));
总是能给出正确的结果。例如,如果N==8,是否有可能log2(N)会给出类似于2.99999999999的结果,最终结果会变成4而不是8?

不准确的操作数与不准确的结果 我想知道如果我使用数学函数,是否会出现类似的问题

实际上,对于基本操作(包括
*
),不存在可能阻止
log2(8)
变为3的问题。但是它存在于
log2
函数中

您混淆了两个不同的问题:

double a = 1 / 3.0;
int b = a * 3;      // b will be 0 here
在上面的示例中,
a
不完全是1/3,因此
a*3
可能不会产生
1.0
。该产品可能碰巧达到了
1.0
,但事实并非如此。然而,如果
a
以某种方式正好是1/3,那么
a
乘以3的乘积将正好是
1.0
,因为这就是IEEE 754浮点运算的工作原理:基本运算的结果是与相同操作数上相同运算的数学结果最接近的可表示值。当精确的结果可以表示为浮点数时,则该表示就是您得到的结果

sqrt和log2的准确性
sqrt
是“基本操作”的一部分,因此
sqrt(4)
在IEEE 754系统中始终是
2.0
,毫无例外

log2
不是基本操作的一部分。IEEE 754标准不保证该函数的实现结果与数学结果最接近。它可以是更远的另一个可表示的数字。因此,如果没有更多关于您使用的
log2
函数的假设,就不可能知道
log2(8.0)
可以是什么

然而,对于诸如
log2
之类的基本函数,大多数具有合理质量的实现都保证了实现的结果在数学结果的1以内。当数学结果不可表示时,这意味着上面或下面的可表示值(但不一定是两者中最接近的一个)。当数学结果完全可表示时(如
3.0
),则此表示仍然是唯一保证返回的表示

因此,关于
log2(8)
,答案是“如果您对
log2
有一个合理的质量实现,您可以预期结果为3.0`”

不幸的是,并不是每个基本函数的每个实现都是高质量的实现。看到这一点,这是由于广泛使用的
pow
实现在计算
pow(10.0,2.0)
时不准确超过1 ULP,从而返回
99.0
而不是
100.0

四舍五入到最接近的整数 接下来,在每种情况下,通过隐式转换将浮点赋值给
int
。此转换在C++标准中定义为截断浮点值(即舍入零)。如果希望浮点计算的结果为整数,则可以在赋值之前将浮点值四舍五入到最接近的整数。它将有助于在误差累积值不大于1/2的所有情况下获得所需答案:

int b = std::nearbyint(log2(8.0));

以一个简单明了的标题来结束这个问题:是的,当使用浮点函数来生成整数最终结果时,您应该担心精度。这些函数即使在基本操作的保证下也不会出现。

< P>不幸的是,通过在C++中删除小数部分,从浮点数到整数的默认转换实际上是疯狂的。 这不好有两个原因:

  • 浮点数实际上非常接近正整数,但低于它的将转换为上一个整数(例如,3-1×10-10=2.9999999将转换为2)

  • 浮点数实际上非常接近于负整数,但高于它将转换为下一个整数(例如,-3+1×10-10=-2.9999999将转换为-2)

  • (1)和(2)的组合还意味着使用
    int(x+0.5)
    将无法合理工作,因为它会将负数取整

    有一个合理的
    round
    函数,但不幸的是返回另一个浮点数,因此需要编写
    int(round(x))

    使用C99或C++11时,可以使用
    lround(x)

    请注意,唯一可以用浮点正确表示的数字是商,其中分母是2的整数幂

    例如,
    1/65536=0.0000152587890625
    可以正确表示,但即使仅
    0.1
    也不可能正确表示,因此涉及该数量的任何计算都将是近似值

    当然,当使用0.1近似值时,可能会抵消偶尔留下的正确结果,但即使只是将0.1的十倍相加,在使用IE进行计算时也不会得到1.0的结果