int转float时精度损失问题 --暨-- 实现sqrt()函数

1、问题

闲来无事,做了道算法题图一乐,很简单,就是实现sqrt()函数的功能,即求解非负整数的平方根,我给出的算法如下:

int mySqrt(int x) {
    if(x == 1)
        return 1;
    float low = 0.0, xtmp =x, high = x;
    float mid = (low + high) / 2.0;
    int re = 0; float comp = 1E-5;
    
    while(high - mid >= 0.0)
    {
        float tmp = mid * mid;
        if(tmp - xtmp > comp)   //tmp大于xtmp
        {
            high = mid;
        }
        else if(xtmp - tmp > comp) //tmp小于xtmp
        {
            low = mid;
        }
        else if( (tmp-xtmp < comp) && (xtmp - tmp < comp) )  //tmp等于xtmp,对于float变量而言,没有绝对的相等
        {
            re = mid;
            break;
        }
        mid = (low + high) / 2.0;
    }
    return re;
}

我是通过二分查找的方式来求解的,但是当我执行到测试用例:x =  2147395599时,计算出来的结果为46340.000000,而不是正确答案46339.99998,虽然只差了不到0.1,但是他们计算出来的平方值却相差比较大的。最后经过调试与分析,发现,在语句float xtmp = x; xtmp的值就已经不是2147395599.000000,而是变成了2147395584.000000,最后猜测可能会与int转float时的精度损失有关系。

2、问题分析

int转float时的精度损失是由float型在内存中的存储方式引起的。关于float类型在内存的存储可以参考博文:https://blog.csdn.net/yezhubenyue/article/details/7436624,下面就解释一下该问题出现的原因:

int x = 2147395599,在内存中的存储为:1111111111111101010100000001111,有31

float xtmp = x,即将x的值赋值给xtmp,所以按照float类型的存储方式,需要将小数点左移30位,变成1.111111111111101010100000001111,由于第24为必须为1,且不存储,实际上是.111111111111101010100000001111

这样xtmp的指数就等于30 ,但存储时需按照30+127=157的格式来存储,即:10011101

由于是正数,所以float类型的首位为0,但float的尾数只有23位(.11111111111110101010000),所以该float型的数据存储格式为:0-10011101-11111111111110101010000

那么该float型二进制串表示的十进制是多少呢,首位为0表示是正数,指数位为10011101 = 157,减去127为30,所以尾数中的小数点需要往右移30位,但实际上只有23位,所以需要补齐,由1.11111111111110101010000 →变为:111111111111110101010000 0000000.0 = 十进制 2147395584.000000

3、解决

把float类型换位double型就可以了。

你可能感兴趣的:(C/C++,编程,Windows,算法)