初学者C难题:为什么25==25?
我在中级C编程课的第一个实验室里有一个简单的任务。我从用户那里获取了一个包含8个double的数组,然后再获取1个double。然后,我检查数组中一个double的平方加上数组中其他double的平方,看看它们是否等于最后一个输入给程序的平方,即额外的double 我的问题是,出于某种原因,当我的两个输入的平方等于额外输入的平方时,我的编译器不这么认为 请让我知道我做错了什么;我将Codelite与gnu gdb调试器和gcc编译器一起使用 样本输入:435初学者C难题:为什么25==25?,c,arrays,gcc,codelite,C,Arrays,Gcc,Codelite,我在中级C编程课的第一个实验室里有一个简单的任务。我从用户那里获取了一个包含8个double的数组,然后再获取1个double。然后,我检查数组中一个double的平方加上数组中其他double的平方,看看它们是否等于最后一个输入给程序的平方,即额外的double 我的问题是,出于某种原因,当我的两个输入的平方等于额外输入的平方时,我的编译器不这么认为 请让我知道我做错了什么;我将Codelite与gnu gdb调试器和gcc编译器一起使用 样本输入:435 #include <stdio
#include <stdio.h>
#include <math.h>
int main(int argc, char **argv)
{
int input[8];
double inputSquared[8];
int pairs[16];
int i, j, k; //i and j are for loop workers.
double x;
int numPairs = 0;
printf("Welcome to Lab 1.\nEnter 8 integers. After each entry, press the enter button.\n");
printf("---------------------------------------\n");
for(i=0; i<8; i++){
printf("Enter integer %d:", i+1);
scanf("%d", &input[i]);
printf("\n");
}
//printf("Now enter one more integer.\n The sum of the squares of the following o this integer squared.\n");
printf("Enter an integer: ");
scanf("%lf", &x);
for(k = 0; k<8; k++){
inputSquared[k] = pow((double)input[k], 2);
}
for(i = 0; i<8; i++){
for(j = i + 1; j<8-1; j++){ //does not check for pairs reflexively. If 1 is in the array, it does not check 1^2 + 1^2.
printf("%lf, %lf; %lf; %lf, %d \n", inputSquared[i], inputSquared[j], pow(x, 2.0), inputSquared[i] + inputSquared[j], ((inputSquared[i] + inputSquared[j]) == ((pow(x, 2.0)))));
if(inputSquared[i] + inputSquared[j] == pow(x, 2.0)){
pairs[2 * numPairs] = input[i];
pairs[2 * numPairs + 1] = input[j];
numPairs++;
}
}
}
if(numPairs == 1)
printf("\nYou have %d pair:", numPairs); // grammar condition for having 1 pair
else
printf("\nYou have %d pairs:\n", numPairs);
for(i = 0; i < numPairs; i++)
printf("(%d,%d)", pairs[2 * i], pairs[2 * i + 1]);
scanf("%lf", &x);
return 0;
}
永远不要比较两个值的精确性 使用ε此处使用的ε值为0.00000001:
if (abs(x - y) < 0.00000001)
{
}
所以
[Bbtw:在这方面有很多类似的问题]你永远不应该比较两个值的准确性 使用ε此处使用的ε值为0.00000001:
if (abs(x - y) < 0.00000001)
{
}
所以
[Bbtw:在这方面有很多类似的问题]当你使用double时,你调用的是所谓的浮点算术。实际上,这是一种不精确的数字表示方法,因为不是每个实数都可以用有限的位数精确表示
因此,例如,有些数字永远无法精确表示,而不是25,您的计算机(而不是编译器)的结果是25.00000000000071,对于所有实际用途来说,都足够接近,但显然不等于25
与IEEE-754浮点相关的其他怪癖很少——将0.1乘以10倍于0.0并不一定能达到1.0,等等
这就是为什么,在实践中,你永远不应该比较等式,但是你应该比较两个数字之间的差值的绝对值是否小于一个小的但足够大的ε
const double espilon = 0.00000001
if (fabs(x - y) < epsilon) {
// Assume approximately equal
}
这是一个很好的基于MATLAB的系统,但是这些概念适用于所有使用IEEE-754的系统,现在,这意味着对所有正在发生的事情的介绍如下:
此外,pow并不是进行平方运算的最合理方式——powx,m在内部计算expm logx,以处理任意实数基和指数。这种特殊的转换正是它降低精度的原因
这也意味着与基于乘法的方法相比,pow的速度慢得可笑 使用double时,调用的是所谓的浮点算术。实际上,这是一种不精确的数字表示方法,因为不是每个实数都可以用有限的位数精确表示
因此,例如,有些数字永远无法精确表示,而不是25,您的计算机(而不是编译器)的结果是25.00000000000071,对于所有实际用途来说,都足够接近,但显然不等于25
与IEEE-754浮点相关的其他怪癖很少——将0.1乘以10倍于0.0并不一定能达到1.0,等等
这就是为什么,在实践中,你永远不应该比较等式,但是你应该比较两个数字之间的差值的绝对值是否小于一个小的但足够大的ε
const double espilon = 0.00000001
if (fabs(x - y) < epsilon) {
// Assume approximately equal
}
这是一个很好的基于MATLAB的系统,但是这些概念适用于所有使用IEEE-754的系统,现在,这意味着对所有正在发生的事情的介绍如下:
此外,pow并不是进行平方运算的最合理方式——powx,m在内部计算expm logx,以处理任意实数基和指数。这种特殊的转换正是它降低精度的原因
这也意味着与基于乘法的方法相比,pow的速度慢得可笑 如果将x的平方计算为:
x * x
甚至
(double)x * (double)x
然后你会得到一个精确的正方形。换句话说,
4 * 4 + 3 * 3 == 5 * 5 => 1 (true)
4.0 * 4.0 + 3.0 * 3.0 == 5.0 * 5.0 => 1 (true)
其实,
5 * 5 == 5.0 * 5.0 => 1 (true)
但是,
因为数学库而不是编译器不会费心检查pow的第二个参数是否恰好是一个小整数。它只是通过计算powx,p=&ee;p*lnx。不幸的是,该值通常不能表示为双精度-事实上,它通常甚至不是一个有理数,因此它实际上根本没有有限的表示形式-数学库决定使用泰勒级数或类似的方法计算一个合理的近似值
所以不仅是pow5.0,2.0有点不准确,计算起来也相当复杂。而5*5只是一条机器指令。你可能会得出自己的结论
很多人会告诉你,永远不要比较浮点值是否相等。这一建议倾向于引导或来自一种心理模型,其中浮点数是一种模糊的、失焦的东西,可以不可预测地波动,几乎就像它受到海森堡不确定性的影响一样。这实际上不是一个虚拟现实
这是思考浮点数的好方法。每个浮点数都是某个有理数的精确表示;事实上,它是一个有理值的精确表示,其分母是2的幂,分子是0或1到2k-1之间的整数,对于大多数现代CPU上的一些较小的双倍k 52
因此,在什么情况下浮点运算是精确的是完全可以预测的。例如,如果已知x是一个绝对值小于一年中秒数的整数,则doublex*x是完全正确的。另一方面,分母减至最低项时为奇数或具有奇数因子的分数永远不能精确表示为浮点数。如果将x的平方计算为:
x * x
甚至
(double)x * (double)x
然后你会得到一个精确的正方形。换句话说,
4 * 4 + 3 * 3 == 5 * 5 => 1 (true)
4.0 * 4.0 + 3.0 * 3.0 == 5.0 * 5.0 => 1 (true)
其实,
5 * 5 == 5.0 * 5.0 => 1 (true)
但是,
因为数学库而不是编译器不会费心检查pow的第二个参数是否恰好是一个小整数。它只是通过计算powx,p=&ee;p*lnx。不幸的是,该值通常不能表示为双精度-事实上,它通常甚至不是一个有理数,因此它实际上根本没有有限的表示形式-数学库决定使用泰勒级数或类似的方法计算一个合理的近似值
所以不仅是pow5.0,2.0有点不准确,计算起来也相当复杂。而5*5只是一条机器指令。你可能会得出自己的结论
很多人会告诉你,永远不要比较浮点值是否相等。这一建议倾向于引导或来自一种心理模型,其中浮点数是一种模糊的、失焦的东西,可以不可预测地波动,几乎就像它受到海森堡不确定性的影响一样。这实际上不是一个很好的思考浮点数的方法。每个浮点数都是某个有理数的精确表示;事实上,它是一个有理值的精确表示,其分母是2的幂,分子是0或1到2k-1之间的整数,对于大多数现代CPU上的一些较小的双倍k 52
因此,在什么情况下浮点运算是精确的是完全可以预测的。例如,如果已知x是一个绝对值小于一年中秒数的整数,则doublex*x是完全正确的。另一方面,分母降到最低项时为奇数或具有奇数因子的分数永远无法精确表示为浮点数。这里的核心问题是,您有一个低质量的数学库,其中pow无法返回良好的结果。在这种特殊情况下,您可以使用x*x代替powx,2来解决这个问题 虽然浮点不能准确地表示所有数字,整数算术也不能,当然,有些数学例程很难实现精确性,但一个好的数学库会努力返回精确的结果 对于pow函数,有许多情况下应特别寻求精度,包括那些结果可精确表示的情况。这包括powx,2等情况,其中x*x完全可以用浮点格式表示
如果x的值具有足够的有效位,因此x*x无法以浮点格式精确表示,或者当您尝试从不可精确表示的输入读取值时,上述解决方法将失败。在这些情况下,其他答案建议您使用容差比较浮点数。但是,这会引入额外的bug。它减少了错误的否定,拒绝不相等的数字,即使它们在计算时是相等的,但代价是增加错误的肯定,接受相等的数字,即使它们是真正不相等的。这里的核心问题是,你有一个低质量的数学库,pow无法返回好的结果。在这种特殊情况下,您可以使用x*x代替powx,2来解决这个问题 虽然浮点不能准确地表示所有数字,整数算术也不能,当然,有些数学例程很难实现精确性,但一个好的数学库会努力返回精确的结果 对于pow函数,有许多情况下应特别寻求精度,包括那些结果可精确表示的情况。这包括powx,2等情况,其中x*x完全可以用浮点格式表示 如果x的值具有足够的有效位,因此x*x无法以浮点格式精确表示,或者当您试图从输入中读取不精确表示的值时,上述解决方法将失败
可恶的。在这些情况下,其他答案建议您使用容差比较浮点数。但是,这会引入额外的bug。它减少了误报,拒绝不相等的数字,即使它们在计算时是相等的,但代价是增加误报,接受相等的数字,即使它们是真正不相等的。感谢您提供此解决方案!我需要改进我的stackoverflow搜索技能,因为这太简单了。计算机可以很容易地表示25。这种情况下的问题是pow实现的质量很差,返回的结果不准确,即使准确的结果完全可以实现。一个好的pow实现对pow5,2的返回正好是25。这就是我在回答底部解释的。@qdot:不,这个回答并没有说本例中的pow实现质量差。它说pow由于在内部计算explogx而失去精度。据推测,爆炸是有意的。此外,它应该是“准确”,而不是“精确”。然而,使用对数和指数并不妨碍pow实现提供准确的结果。为此,它必须使用扩展精度。因此,使用对数和指数不会导致质量差;不在设计上下工夫会导致质量差。是的,你说得对——准确,而不是精确。然而,在一般情况下,使用对数和指数确实会妨碍pow实现提供准确的结果——不可能使用任何二进制浮点数精确表示log2。此外,答案不表示expm logx感谢您提供此解决方案!我需要改进我的stackoverflow搜索技能,因为这太简单了。计算机可以很容易地表示25。这种情况下的问题是pow实现的质量很差,返回的结果不准确,即使准确的结果完全可以实现。一个好的pow实现对pow5,2的返回正好是25。这就是我在回答底部解释的。@qdot:不,这个回答并没有说本例中的pow实现质量差。它说pow由于在内部计算explogx而失去精度。据推测,爆炸是有意的。此外,它应该是“准确”,而不是“精确”。然而,使用对数和指数并不妨碍pow实现提供准确的结果。为此,它必须使用扩展精度。因此,使用对数和指数不会导致质量差;不在设计上下工夫会导致质量差。是的,你说得对——准确,而不是精确。然而,在一般情况下,使用对数和指数确实会妨碍pow实现提供准确的结果——不可能使用任何二进制浮点数精确表示log2。此外,答案不表示expm logx