zoukankan      html  css  js  c++  java
  • 浮点数的存储与表达

    计算机中的浮点数只是无限接近真实值的近似值。为什么呢?首先来看一下浮点数在计算机中是如何存储的。

    浮点数的存储        

    计算机中浮点数的存储遵循IEEE754浮点数标准。单精度用32位存储,而双精度用64位存储。具体的存法如下(以单精度为例):

    单精度(32位) = 1位符号位+8位指数位+23位尾数位,见下图。

    其中,符号位用来表示数值的符号。关于指数位和尾数位,需要先了解计算机表示浮点数的原理。计算机中,浮点数是二进制科学计数法来表示的。比如10.5(十进制)=1010.1(二进制)=1.0101 * 2^3.那这里1.0101就叫尾数,3就叫指数。这两个值会分别存入浮点数的指数和尾数区域。要注意的是:

    1. 因为尾数第一位肯定为1,所以在存储的时候省略,在读取数据的时候在加上。这样会将尾数部分实际的存储能力扩大到24位。

    2. 指数也有可能为负数,8位只能表示-127到128。在存储指数的时候实际是存储的原数据+127的值,读取指数数据的时候再减去127。例如:指数3,实际存储的值是3+127 = 130

    指数和尾数的存储能力直接决定了其能表示数字的范围和精度:

    1. 数字范围:(-1)*2^(符号位) * 尾数最大数(1.1111...) * 指数最大数(2^128) = (-3.4 * 10^38, 3.4 * 10^38)

    2. 精度:尾数最多能存24位,最大值为2^24 = 16777216  这个数字能完整表示 10^7以内的整型数字,因此精度为7位

    C# float类型参考

    浮点数的精度误差    

     小数的二进制表达:前面其实提到过,10.5的二进制是1010.1,整数部分的算法是除二取余(余数为当前位的二进制值)。小数部分的算法是乘二取整数部分作为当前位的二进制值,小数部分继续进行乘二取整。所以10.5的二进制表达为1010.1。

    但是现实情况是大部分小数乘2取整的最后是一个无限循环。比如:0.99,可以一直执行乘二取整,最后也不会得到一个乘二后值为1的数字。所以这种值会变为二进制时是无法存储其精确值的,因为尾数的存储位数是有限制的。(这里的单精度为23位) 因此会有精度丢失,当从内存中取值时得到的数值其实是无限接近于真实值的近似值。

    比如执行如下代码:

    float fValue = 0.99F;
    Console.WriteLine("float:" + fValue.ToString("G8"));
    // output: 0.99000001

    原因是什么呢?运用上面的存储原理可以做如下解释:0.99转换为二进制后的值为(只列出前25位):1111110101110000101000111(25bit),但是单精度只能存24位,那实际存储的值为:111111010111000010100011(24bit)。这里就丢掉了一位数据。当还原为10进制数时就会不够准确。其实这里我尝试还原了一下,但是得到的结果和预期不太一样(结果为:0.9899999499320983886716),可能计算过程中本身也有误差导致。还原代码如下:

    static void BinToDecimal(string binString)
    {
                decimal result = 0;
                int index = -1;
                foreach (var c in binString)
                {
                    if (c == '1')
                    {
                        result += (decimal)Math.Pow(2, index);
                    }
    
                    index -= 1;
                }
    
                Console.WriteLine(result);
    }

    上述代码如果我们这样写:

    float fValue = 0.99F;
    Console.WriteLine("float:" + fValue.ToString());
    // output:0.99

    结果就是对的,原因是fValue.ToString() 默认是显示7位精度。这样得到的结果就是0.99。

    各种数据类型的ToString()方法默认显示精度见MSDN

    Decimal类型        
    在财务计算等高精度要求的场景,浮点数的误差可能会造成严重后果。所以decimal类型适用于这种高精度的场景。decimal的存储方式是:

    符号位(1bit) + 数值位(96bit) + 放大因子(31bit) = 128bit

    其中数值位用来存储要表示的完整数字去掉小数点后的整数表示,放大因子决定小数点的位置。因此所能存储的数字范围为(-2^96,2^96)对应十进制:(-79228162514264337593543950336,79228162514264337593543950336).所以decimal一共是能表示精度28位的数字。不存在十进制与二进制的转换,所以不会有精度误差。所以对数值精度要求高时推荐使用decimal数据类型。

  • 相关阅读:
    DM数据库disql的使用 Disql disql 达梦数据库Disql
    移动端禁止蒙层下的页面滚动
    移动端如何自动适配px
    使用Vant做移动端对图片预览ImagePreview和List的理解
    uniapp中使用uView组件库
    h5使用vuephotopreview 做全屏预览
    jsonview的实现
    PC端自适应使用rem 移动端适配升级版
    axios解决跨域问题(vuecli3.0)
    vs code 配置git path
  • 原文地址:https://www.cnblogs.com/Code-life/p/8874101.html
Copyright © 2011-2022 走看看