C/C++ - int转float时精度损失问题

C/C++ 同时被 3 个专栏收录
36 篇文章 0 订阅
16 篇文章 0 订阅
6 篇文章 0 订阅

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型在内存中的存储方式引起的。

2.1、float类型在内存中的存储方式

关于float类型在内存的存储可以参考博文:float类型在内存中的表示

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

float xtmp = x,即将x的值赋值给xtmp,所以需要按照float类型的存储方式。

首先,需要将小数点左移30位(因为是正数,所以将小数点左移到第一个有效数字之后,整数的小数点默认是在最后),变成1.111111111111101010100000001111;

按照规定,第1个有效数字为必须为1,且不存储,实际上是 .111111111111101010100000001111(尾数部分);

然后就能计算出指数部分就位30(因为左移了30位) ,但存储时是按照偏移量存储的,需按照30+127=157的格式来存储,即:10011101(指数部分)

由于是正数,所以float类型的首位为0,但由于float的尾数只有23位,所以需要从30位的尾数部分(111111111111101010100000001111)截取前23位数字,所以就变成了.11111111111110101010000

最终该float型的数据存储格式为:0-10011101-11111111111110101010000 (首位0为符号位,中间8位位指数部分,最后23位位尾数部分)

2.2、问题原因

由2.1章节可知,最终该float类型的数据二进制形式为:0-10011101-11111111111110101010000

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

3、解决

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

  • 1
    点赞
  • 0
    评论
  • 4
    收藏
  • 打赏
    打赏
  • 扫一扫,分享海报

参与评论 您还未登录,请先 登录 后发表或查看评论
©️2022 CSDN 皮肤主题:技术黑板 设计师:CSDN官方博客 返回首页

打赏作者

bailang_zhizun

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值