简体   繁体   English

C - 平方根函数无法正常工作

[英]C - Square root function not working properly

I've written a function called "mysqrt(double r)" which calculates the square root of "r".我编写了一个名为“mysqrt(double r)”的函数来计算“r”的平方根。 However, the result is different from the one given by the standard sqrt() function from the math.h library.但是,结果与 math.h 库中的标准 sqrt() 函数给出的结果不同。

The code is the following:代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
 
double mysqrt (double r);
 
int main()
{
   double r1, r2;
 
   r1 = sqrt (2.0);
   r2 = mysqrt (2.0);
 
   if (r1 == r2)
   {
      printf ("OK! Result = %.40lf\n", r1);
   }
   else
   {
      printf ("!? r1 = %.40lf, r2 = %.40lf\n", r1, r2);
   }
 
   return EXIT_SUCCESS;
}
 
double mysqrt (double r)
{
   double n1, n2;
 
   n1 = r;
   n2 = 1.0;
   while (n1 - n2 > 0)
   {
      n1 = (n1 + n2) / 2.0;
      n2 = r / n1;
   }
   return n1;
}

And the output is: ?. r1 = 1,4142135623730951454746218587388284504414. r2 = 1.4142135623730949234300169337075203657150输出是: ?. r1 = 1,4142135623730951454746218587388284504414. r2 = 1.4142135623730949234300169337075203657150 ?. r1 = 1,4142135623730951454746218587388284504414. r2 = 1.4142135623730949234300169337075203657150

I've tried changing the types from double to long double, but it didn't work.我试过将类型从 double 更改为 long double,但没有用。

You want to approximate the irrational number sqrt(2) with a IEEE-754 binary64 representation.您想要使用 IEEE-754 二进制 64 表示来近似无理数sqrt(2) You have two possibilities:你有两种可能性:

approx1 double: 0 01111111111 0110101000001001111001100110011111110011101111001100
approx2 double: 0 01111111111 0110101000001001111001100110011111110011101111001101

What are those numbers?这些数字是多少? Let's convert them back to decimal:让我们将它们转换回十进制:

approx1 (exact): 1.41421356237309492343001693370752036571502685546875
sqrt(2)        : 1.4142135623730950488016887242096980785696718753769480731766797379...
approx2 (exact): 1.4142135623730951454746218587388284504413604736328125

They are both pretty close.他们都非常接近。 In fact they are the two closest numbers you can obtain with binary64.事实上,它们是您可以使用 binary64 获得的最接近的两个数字。 Which is better?哪个更好?

abs(sqrt(2)-approx1): 0.0000000000000001253716717905021777128546450199081980731766797379...
abs(sqrt(2)-approx2): 0.0000000000000000966729331345291303718716885982558644268233202620...

It seems that approx2 is slightly closer to the real value than approx1 .似乎approx2approx1更接近实际值。 For most purposes they are pretty equivalent.在大多数情况下,它们是相当等价的。

I've used this website for the conversion and WolframAlpha for the comparisons.我使用这个网站进行转换,使用WolframAlpha进行比较。

EDIT编辑

Floating point math is hard .浮点数学很难 Notice this:注意这一点:

int main(void)
{
   double r1, r2;
 
   r1 = sqrt (2.0);
   r2 = mysqrt (2.0);
   printf ("sqrt(2):\nr1 = %.40lf\nr2 = %.40lf\n", r1, r2);

   r1 *= r1;
   r2 *= r2;
   printf ("Square them:\nr1 = %.40lf\nr2 = %.40lf\n", r1, r2);

   r1 = fabs(r1 - 2.0);
   r2 = fabs(r2 - 2.0);
   printf ("Subtract 2 (abs):\nr1 = %.40lf\nr2 = %.40lf\n", r1, r2);

   return EXIT_SUCCESS;
}

The output is:输出是:

sqrt(2):
r1 = 1.4142135623730951454746218587388284504414
r2 = 1.4142135623730949234300169337075203657150
Square them:
r1 = 2.0000000000000004440892098500626161694527
r2 = 1.9999999999999995559107901499373838305473
Subtract 2 (abs):
r1 = 0.0000000000000004440892098500626161694527
r2 = 0.0000000000000004440892098500626161694527

So, the squared version of the two approximations of sqrt(2) are equally distant from 2. The same happens for sqrt(8) , for example.因此, sqrt(2)的两个近似值的平方版本与 2 的距离相等。例如,对于sqrt(8)也是如此。

EDIT 2编辑 2

Floating point math is really hard .浮点数学真的很难 Your function enters an infinite loop for some inputs.您的函数针对某些输入进入无限循环。 Try for example 7.0 .尝试例如7.0 You can fix it as follows:您可以按如下方式修复它:

double mysqrt(double r)
{
    double n1 = r;
    double n2 = 1.0;
    double old = n2;
    while (old != n1 && n1 - n2 > 0) {
        old = n1;
        n1 = (n1 + n2) / 2.0;
        n2 = r / n1;
    }
    return n1;
}

I re-wrote the mysqrt function like this:我像这样重写了mysqrt函数:

double mysqrt (double r)
{
   double n1, n2;
   n1 = r;
   n2 = 1.0;
   while (absolute_value(n1 - n2) > EPSILON)
   {
      n1 = (n1 + n2) / (double) 2;
      n2 = r / n1;
   }
   return n1;
}

double absolute_value (double n)
{
    if (n < 0)
    {
        n *= -1;
    }
    return n;
}

with

#define EPSILON 1e-15

By doing so the output becomes:通过这样做,输出变为:

OK. Result = 1.4142135623730951454746218587388284504414

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM