zoukankan      html  css  js  c++  java
  • C/C++: 彩蛋:1.float型不能表示16777217这个数 2.gcc/g++不完全兼容C标准

    ###彩蛋一#####

    今天debug一个程序,发现一个很有趣的问题:

       float 类型不能表示 16777217这个数。

      比如:

    float i=0;
    while(true){
        i++;
        if( i >= 16777217 ){
            break;
        }
    }

      如果这样写的话,就会死循环,因为 float 型浮点数不能表示16777217这个数,所以当  i  等于 16777216的时候,往上加 1 ,还是16777216,所以永远跳不出那个while循环。

    为什么会这样子呢?这就牵扯到浮点数的表示问题了。引用stackoverflow上一个人的回答如下(链接):

    Short roundup of IEEE-754 floating point numbers (32-bit) off the top of my head:

    • 1 bit sign (0 means positive number, 1 means negative number)
    • 8 bit exponent (with -127 bias, not important here)
    • 23 bits "mantissa"
    • With exceptions for the exponent values 0 and 255, you can calculate the value as: (sign ? -1 : +1) * 2^exponent * (1.0 + mantissa)
      • The mantissa bits represent binary digits after the decimal separator, e.g. 1001 0000 0000 0000 0000 000 = 2^-1 + 2^-4 = .5 + .0625 = .5625 and the value in front of the decimal separator is not stored but implicitly assumed as 1 (if exponent is 255, 0 is assumed but that's not important here), so for an exponent of 30, for instance, this mantissa example represents the value 1.5625

    Now to your example:

    16777216 is exactly 224, and would be represented as 32-bit float like so:

    • sign = 0 (positive number)
    • exponent = 24 (stored as 24+127=151=10010111)
    • mantissa = .0
    • As 32 bits floating-point representation: 0 10010111 00000000000000000000000
    • Therefore: Value = (+1) * 2^24 * (1.0 + .0) = 2^24 = 16777216

    Now let's look at the number 16777217, or exactly 224+1:

    • sign and exponent are the same
    • mantissa would have to be exactly 2-24 so that (+1) * 2^24 * (1.0 + 2^-24) = 2^24 + 1 = 16777217
    • And here's the problem. The mantissa cannot have the value 2-24 because it only has 23 bits, so the number 16777217 just cannot be represented with the accuracy of 32-bit floating points numbers!

    假设你的机器的浮点数表示是符合IEEE-754标准的(一般都是),那么,表示16777216这个数的时候,情况是这样的:

    • sign = 0(表示正数)
    • exponent=24
    • mantissa(尾数) = .0
    • 结合起来就是 (+1) * 2^24 * (1.0 + .0) = 2^24 = 16777216

    现在,要给这个数 + 1 的时候会发生什么?

    显然不能是 2^25,那就太大了。

    按照浮点数运算的特点,“方向” 应该是这样: (+1) * 2^24 * ( 1.0 + 2^(-24) ) = 2^24 + 1 = 16777217 。然而,mantissa(尾数)部分只有 23 位,凑不成一个 1 ,所以,float类型根本就不能表示 16777217这个数。

    BONUS:如果要表示16777218这个数呢?这是可以的,只需要:(+1) * 2^24 * ( 1.0 + 2^(-23) ) = 2^24 + 2 = 16777218

    同样的,double型的数也是,不能表示 199,254,740,993 (2^53+1)这个数(符合IEEE-754标准的double型的尾数(mantissa)是53位的)

    参考:stackoverflow_1, stackoverflow_2

    ####彩蛋二####

    用gcc编译C程序,按标准,假如是C89(-std=c89 / -ansi)那么,是不能使用Variable Length Array(VLA)的,也就是,不能这样:

    void foo(int n){
        int arr[n];
        ...
    }

    只有C99之后才能这样。

    然而,gcc是不遵守这个规范的【1】【2】,即使你使用了-std=c89.....(FUCK)......如果要按照C89规范来,要加上 -pedantic / -pedantic-errors。

    在C++中,无论哪个版本,都不支持VLA,然而g++还是默认开启了 (FUCK....)

    Clang也是,FUCK ......

    :)

  • 相关阅读:
    django页面分类和继承
    django前端从数据库获取请求参数
    pycharm配置django工程
    django 应用各个py文件代码
    CF. 1428G2. Lucky Numbers(背包DP 二进制优化 贪心)
    HDU. 6566. The Hanged Man(树形背包DP DFS序 重链剖分)
    小米邀请赛 决赛. B. Rikka with Maximum Segment Sum(分治 决策单调性)
    区间树 学习笔记
    CF GYM. 102861M. Machine Gun(主席树)
    2016-2017 ACM-ICPC East Central North America Regional Contest (ECNA 2016) (B, D, G, H)
  • 原文地址:https://www.cnblogs.com/walkerlala/p/5465252.html
Copyright © 2011-2022 走看看