我的中级C编程课程中的第一个实验室有一个简单的任务。我正在从用户那里获得8个双打阵列,然后再增加1个双倍。然后我检查数组中一个double的平方加上数组中其他双精度的平方,看它们是否等于给予程序的最后一个输入的平方(附加的双精度数)。
我的问题是,由于某种原因,当我的两个输入平方等于额外的输入平方时,我的编译器不这么认为。
请让我知道我在这里做错了什么;我正在使用Codelite和gnu gdb调试器和gcc编译器。
样本输入:4 3 3 3 3 3 3 3 5
#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 :(得分:4)
如果你计算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)
但是,
5 * 5 == pow(5.0, 2.0) => 0 (false)
因为数学库(不编译器)不打算检查pow
的第二个参数是否恰好是一个小整数。它只是通过计算pow(x, p) = ⅇp*ln(x)
来计算价值。不幸的是,这个值通常不能表示为双重 - 事实上,它通常不是一个有理数,所以它根本没有真正的有限表示 - 并且数学库合理地结算由泰勒级数(或类似的东西)计算的近似逼近。
因此pow(5.0, 2.0)
不仅略微不准确,而且计算起来也相当复杂。而5 * 5
只是一个机器指令。你可以得出自己的结论。
很多人会告诉你,你应该“永远”比较浮点值是否相等。这个建议倾向于导致(或来自)一个心理模型,其中浮点数是一种模糊的,失焦的东西,它可以不可预测地波动,几乎就像它受到海森堡不确定性的影响一样。这实际上并不是一种考虑浮点数的好方法。每个浮点数都是一些理性值的精确表示;事实上,它是一个合理值的精确表示,其分母是2的幂,其分子为0或1到2之间的整数 k -1对于某些小k(52为双精度)最现代的CPU)。
因此,在哪种情况下浮点运算是精确的是可以预测的。例如,如果已知x
是一个绝对值小于一年中秒数的整数,那么(double)x * x
就是正确的。另一方面,分母(当减少到最低项)是奇数(或具有奇数因子)的分数永远不能精确地表示为浮点数。
答案 1 :(得分:2)
您绝不应该比较精确度的双重值。
使用epsilon(此处使用的epsilon值为0.00000001):
if (abs(x - y) < 0.00000001)
{
}
所以,
if ( abs(inputSquared[i] + inputSquared[j] - pow(x, 2.0)) < 0.0000001)
{
[Bbtw:SO上有很多类似的问题]
答案 2 :(得分:2)
当您使用double
时,您正在调用所谓的“浮点算术”。实际上,这是表示数字的一种不精确的方式,因为不是每个实数都可以用有限的位精确表示。
因此,某些数字,例如,永远无法准确表示,而不是“25”您的计算机(而不是编译器),有25.00000000000071的结果 - 足够接近所有实际目的,但显然不等于25.
与IEEE-754浮点相关的其他怪癖很少 - 向0.0增加0.1倍并不一定达到1.0等等。
这就是为什么在实践中你永远不应该比较相等,但你应该比较两个数字之间的差值的绝对值是否小于(小,但足够大)epsilon。
const double espilon = 0.00000001
if (fabs(x - y) < epsilon) {
// Assume approximately equal
}
一个好的(基于MATLAB,但概念适用于使用IEEE-754的所有内容,现在,这意味着无处不在)介绍正在发生的事情:
https://www2.bc.edu/~quillen/sp11/mt414/pres/floatarith.pdf
此外,pow()
不是最合理的方格方式 - pow(x, m)
内部计算exp(m log(x))
以处理任意实数基数和指数。这种特殊的转换正是它失去精度的原因。
与基于乘法的方法相比,这也意味着pow()
非常慢。
答案 3 :(得分:1)
这里的核心问题是你有一个低质量的数学库,其中pow
无法返回好的结果。通过使用x*x
代替pow(x, 2)
,您可以在此特定情况下解决此问题。
虽然浮点数不能完全代表所有数字(当然也不能用整数算术)和一些数学例程很难实现准确性,但是一个好的数学库力求返回准确的结果。
对于pow
函数,在许多情况下应特别寻求准确性,包括结果可准确表示的那些。这包括pow(x, 2)
等情况,其中x*x
可以用浮点格式表示。
对于具有足够有效位的x
的值,上面的解决方法将失败,x*x
无法以浮点格式准确表示(或者当您尝试从输入中读取值时完全可以表示)。在这些情况下,其他答案建议您使用容差来比较浮点数。但是,这会引入额外的错误。它减少了假阴性(拒绝数字为不相等,即使它们在精确计算时是相等的),代价是增加误报(接受数字相等,即使它们确实是不相等的)。