【问题标题】:Square root algorithm in CC中的平方根算法
【发布时间】:2016-10-19 23:37:28
【问题描述】:

出于练习的原因,我用 C 编写了以下算法:

#include <stdio.h>
#include <stdlib.h>


int main(){

double x=0;

printf("Enter the number: ");

scanf("%lf", &x);

int i = 0;

double v = 0;
double n=0;
int grenze = 12;
double z = 10;
/*
for(i=1; i<(x/2+1); i++){
    v=i;
    if((v*v) <= x){
        n = i;
    }
}

v=n;
*/
for(i=1; i<grenze+1; i++){
    z = z * 0.1;

    while(v*v<x){
        v = v + z;
        if(v*v<x){
            n = v;
        }
    }
    v=n;
}
printf("%.10f\n", n);



}

这很好用,但是对于大于某个值的数字(我不知道什么时候开始),例如 50.000.000.000,程序会冻结。

这里有什么我看不到的吗?

【问题讨论】:

  • 您是否使用过调试器来帮助您?它旨在帮助深入研究此类问题。
  • 这与使用调试器有什么关系?调试器(即 gdb)可以在终端中运行。
  • 对于足够大的数字,v = v + z; 是无操作的 — v 不会改变。这会使您的循环运行很长时间。该算法不是一个好的选择。我不相信它适用于像1E-70 这样的数字(你已经证明它不适用于像1E+70 这样的数字)。请注意,在大多数支持 IEEE 浮点数学的机器上,双精度的范围高达 1E±300 或更大。
  • 浮点数的十进制位数有限——double 通常约为 16 个。如果将1E+161E-16相加,则结果为1E+16;没有足够的有效数字来存储额外的信息。您以5E+10 为起点;你生成分数z = 1.0 然后0.10.01,...0.000 000 000 01 或附近(朋友之间的数量级是多少?)。当您四处添加最小值时,您最终不会更改任何内容。
  • 有关该主题的规范文档是What Every Computer Scientist Should Know About Floating-Point Arithmetic 或来自ACM。并且应该有一个可以重复的问答。我还没有确定。我会将我的 cmets 转换为答案。

标签: c algorithm square-root


【解决方案1】:

对于足够大的数字v和足够小的数字zv = v + z;是无操作的——v 不会改变。这会让你的循环运行很长时间。

该算法不是一个好的选择——您应该查找Newton-Raphson 方法。我不相信您的算法适用于像1E-70 这样的极端数字(并且您已经证明它不适用于像1E+70 这样的数字)。请注意,在大多数支持 IEEE 浮点的机器上,双精度的范围高达 1E±300 或更大。

您能否解释一下为什么v = v + z 在一定限制下是无操作的?

浮点数的十进制位数是有限的——通常大约为 16 位。如果将1E+161E-16相加,则结果为1E+16;没有足够的有效数字来存储额外的信息。您以5E+10 为起点;你生成分数z = 1.0 然后0.10.01,...0.000 000 000 01 或附近(朋友之间的数量级是多少?)。当您四处添加最小值时,您最终不会更改任何内容。

有关该主题的规范文档是What Every Computer Scientist Should Know About Floating-Point Arithmetic 或来自ACM

【讨论】:

    【解决方案2】:

    如果您在 while 循环中编写 printf 语句以显示 v,您会看到它在某个时候停止更改(这很糟糕,因为您使用它来退出循环!)。

    我将printf("DEBUG: entered for loop, v=%lf\n", v); 放入while 循环并看到了这个:

    DEBUG: entered for loop, v=173205.080757
    DEBUG: entered for loop, v=173205.080757
    DEBUG: entered for loop, v=173205.080757
    DEBUG: entered for loop, v=173205.080757
    DEBUG: entered for loop, v=173205.080757
    DEBUG: entered for loop, v=173205.080757
    DEBUG: entered for loop, v=173205.080757
    DEBUG: entered for loop, v=173205.080757
    

    您添加的 z 非常小,以至于当 v 为 173205 时,它实际上并没有改变值。您可以通过将v的类型更改为long double来扩展范围;但是,由于我们只有一定数量的位可以使用,所以仍然有一个上限,向其添加足够小的数字不会改变可表示的值。

    【讨论】:

    • 更改为 long double 是一种临时创可贴——将源编号从 5E10 更改为 5E50 和 long double 将无济于事。它也不能解决小源数的问题。
    • 非常真实的乔纳森,我迅速对其进行了编辑以反映这一现实。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-11-14
    • 2021-08-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-03
    • 1970-01-01
    相关资源
    最近更新 更多