V2EX = way to explore
V2EX 是一个关于分享和探索的地方
Sign Up Now
For Existing Member  Sign In
laoyuan
V2EX  ›  PHP

PHP 果然是世界上最好的语言,再也不相信 var_dump() 了

  •  1
     
  •   laoyuan · Jan 21, 2015 · 6215 views
    This topic created in 4120 days ago, the information mentioned may be changed or developed.
    <?php
    $a = 57; //或者 $a = ‘57’;
    $b = '0.57' * 100;
    $c = round('0.57' * 100);
    var_dump( $b, $c, $a == $b, $a == $c);
    ?>

    float(57)
    float(57)
    bool(false)
    bool(true)

    谁能解释为什么加 round 就 true 了?PHP Version 5.5.20
    Supplement 1  ·  Jan 21, 2015
    其实这个问题是:为什么 round() 返回一个 float,但和 int 比较时总是相等的,别的 float 却没有这个技能
    Supplement 2  ·  Jan 21, 2015
    我大概明白这个问题了:int 和 float 进行比较时,把 int 转为 float 值再比较,而 round() 返回的 float 值恰恰是其对应整数的浮点值,所以 round() 尽管是 float,但和 int 比较时总是相等的。
    44 replies    2015-02-22 23:49:44 +08:00
    bombless
        1
    bombless  
       Jan 21, 2015
    你看,你这就属于学艺不精了……

    我 C 语言一样复现给你看
    #include <stdio.h>
    double round(double);
    int main() {
    double a = 57;
    double b = .57 * 100;
    double c = round(.57 * 100);
    printf("%f, %f, %d, %d", b, c, a == b, a == c);
    return 0;
    }

    输出是 57.000000, 57.000000, 0, 1

    你需要学习的是浮点数,不是 PHP
    eslizn
        2
    eslizn  
       Jan 21, 2015
    round返回的是浮点啊,被蠢哭
    laoyuan
        3
    laoyuan  
    OP
       Jan 21, 2015
    @bombless
    为什么加了 round 的浮点就总是和 int 相等呢,它比普通的浮点高级在哪里??
    tabris17
        4
    tabris17  
       Jan 21, 2015
    你看看 $b == $c 的结果,会有惊喜
    laoyuan
        5
    laoyuan  
    OP
       Jan 21, 2015
    @eslizn 是啊,返回的是浮点,但进行比较的时候又表现出一副 int 的样子来
    laoyuan
        7
    laoyuan  
    OP
       Jan 21, 2015
    @tabris17 没有惊喜,至少没打脸
    bombless
        8
    bombless  
       Jan 21, 2015   ❤️ 1
    @laoyuan 不是高级不高级的……

    round之后就是整数了。

    浮点数就是二进制表示的小数。你打印出来的是十进制的小数。
    所以接下来我就按二进制小数和十进制小数讲解了。
    另外我说的都是有限小数,下面看的时候要把二进制小数理解成二进制表示的有限小数。十进制的情况也同理。


    只有它是整数的时候,二进制才能和十进制表示无损地互换——就是说,可以把二进制小数换成一个十进制表示,再把转换结果换回一个二进制表示,这个表示能和最早这个二进制表示是一样的。

    以上说的是一般的情况。实际上针对上面说的这种转换,已经有算法可以实现对任意浮点数做这种“无损”的转换了。但是这个转换有一些缺点:
    1. 比较晚才发现了这样的算法。这个看似简单的问题对人类的大脑来说还是比较复杂,
    2. 换出来的十进制表示基本上都很长,
    3. 性能不太好。


    因此一般显示的浮点数,虽然是有办法精确表示的,但是出于实用的目的一般选择不精确表示。
    这就是为什么 c 和 b 不一样,但打印的结果是一样的。


    所以如果你需要表示整数,那就要特意的取整。不知道这么说解释清楚没有。
    wenjuncool
        9
    wenjuncool  
       Jan 21, 2015
    学艺不精了吧,浮点数本来就不精确
    tabris17
        10
    tabris17  
       Jan 21, 2015
    @laoyuan 哎。烦请运行下var_export(0.57*100);查看结果

    你需要补课啊 http://zh.wikipedia.org/wiki/IEEE_754
    66beta
        12
    66beta  
       Jan 21, 2015
    官网手册的示例
    <?php
    echo round(3.4); // 3
    echo round(3.5); // 4
    echo round(3.6); // 4
    echo round(3.6, 0); // 4
    echo round(1.95583, 2); // 1.96
    echo round(1241757, -3); // 1242000
    echo round(5.045, 2); // 5.05
    echo round(5.055, 2); // 5.06
    ?>
    ooh
        13
    ooh  
       Jan 21, 2015
    我想楼主你要没想明白的是这个://(int)('0.57' * 100)=56//
    laoyuan
        14
    laoyuan  
    OP
       Jan 21, 2015
    我大概明白这个问题了,int 和 float 进行比较时,把 int 转为 float 值再比较,而 round() 返回的 float 值恰恰是其对应的整数的浮点值,所以 round() 尽管是 float,但和 int 比较时总是相等的。
    FrankFang128
        15
    FrankFang128  
       Jan 21, 2015 via Android
    浮点数就不要比较是否相等了
    laoyuan
        16
    laoyuan  
    OP
       Jan 21, 2015
    @FrankFang128 不,就用 round 比较,生产环境都是这么做的
    tabris17
        17
    tabris17  
       Jan 21, 2015   ❤️ 1
    其实这个问题是:为什么 round() 返回一个 float,但和 int 比较时总是相等的,别的 float 却没有这个技能

    ===============

    <?php
    var_dump(57.0 == 57, 0.57*100 == 57.0);

    怎么就别的float没这个技能了
    lijinma
        18
    lijinma  
       Jan 21, 2015   ❤️ 1
    @laoyuan 楼主,你最后的这个理解是正确的,上面有几个弟兄问非所答。。真是无语。
    qwlhappy
        19
    qwlhappy  
       Jan 21, 2015
    其实应该是var_dump的锅
    laoyuan
        20
    laoyuan  
    OP
       Jan 21, 2015
    @qwlhappy 我也发现了! 用 var_export 就好了
    mcfog
        21
    mcfog  
       Jan 21, 2015   ❤️ 1
    简单来说

    永远不要用`==`

    无论是JS还是PHP

    毕竟他们都是世界上最好的语言
    laoyuan
        22
    laoyuan  
    OP
       Jan 21, 2015
    @mcfog 严重同意LS
    Delbert
        23
    Delbert  
       Jan 21, 2015
    我试了一下
    $a = 5.7;
    $b = 0.57 * 10;
    $c = round(0.57 * 10, 1);
    var_dump( $a,
    $b,
    $c,
    $a == $b,
    $a === $b,
    $a == $c,
    $a === $c);

    结果是:
    float(5.7)
    float(5.7)
    float(5.7)
    bool(false)
    bool(false)
    bool(true)
    bool(true)

    没想明白ing...
    bombless
        24
    bombless  
       Jan 21, 2015
    @Delbert a 和 c 是整数,b 不是,就这么简单。
    laoyuan
        25
    laoyuan  
    OP
       Jan 21, 2015
    @Delbert
    5.7 和 round(0.57 * 10, 1) 是一个数,都是5.7对应的 float 值,而0.57 * 10 这种对 float 进行运算得到的 float,就是一个不可预知的数了,总之 float 的运算很神奇
    bombless
        26
    bombless  
       Jan 21, 2015
    噗,我24楼看错了,不好意思。

    如果真的需要比较浮点数,一般是确定一个可容忍的精度,然后根据这个精度比较两个浮点数的差。

    如果可能的话最好就比较整数,像云风的ejoy游戏引擎为了避开这个问题就把所有的数乘以1024,这样以一定损失为代价,全部都进行整数的比较。
    货币的话常常就直接用十进制表示,不用浮点数了。
    jevonszmx
        27
    jevonszmx  
       Jan 21, 2015
    http://docs.php.net/manual/zh/language.types.float.php
    php官方文档红字警告:

    浮点数的精度

    浮点数的精度有限。尽管取决于系统,PHP 通常使用 IEEE 754 双精度格式,则由于取整而导致的最大相对误差为 1.11e-16。非基本数学运算可能会给出更大误差,并且要考虑到进行复合运算时的误差传递。
    此外,以十进制能够精确表示的有理数如 0.1 或 0.7,无论有多少尾数都不能被内部所使用的二进制精确表示,因此不能在不丢失一点点精度的情况下转换为二进制的格式。这就会造成混乱的结果:例如,floor((0.1+0.7)*10) 通常会返回 7 而不是预期中的 8,因为该结果内部的表示其实是类似 7.9999999999999991118...。
    所以永远不要相信浮点数结果精确到了最后一位,也永远不要比较两个浮点数是否相等。
    picasso250
        28
    picasso250  
       Jan 21, 2015
    看到楼主这样的语言使用者我就明白为什么PHP是世界上最好的语言了
    endrollex
        29
    endrollex  
       Jan 21, 2015
    除非你能搞清这个底层是怎么实现的,是不是bitwise comparison,否则返回什么都没错,浮点根本不能用 ==
    Kilerd
        30
    Kilerd  
       Jan 21, 2015 via Android
    @bombless double输出要用%lf
    laoyuan
        31
    laoyuan  
    OP
       Jan 21, 2015
    @picasso250
    7年PHP经验不是吹的!
    laoyuan
        32
    laoyuan  
    OP
       Jan 21, 2015
    @endrollex 浮点根本不能用 ==
    round 之后就能用了啊,虽然 round 得到的也是浮点
    ioth
        33
    ioth  
       Jan 22, 2015
    一半语言无所谓,一半比较说不同,不让比较的除外。
    ryd994
        34
    ryd994  
       Jan 22, 2015   ❤️ 1
    正常写程序都是不能直接比较浮点的,要比较的话都是 abs(a-b)<某个精确度
    即使是round的结果,如果是浮点数,也无法保证今后就是相等的
    这在任何语言里都是,大多数编程教学里也一定会讲到这一点,你写了7年程序还不知道……
    生产环境里比较round结果……呵呵……
    PHP是世界上最好的语言,可见一斑
    laoyuan
        35
    laoyuan  
    OP
       Jan 22, 2015
    @ryd994 那要 round 还有啥用啊,那我转为字符串再比较吧
    ryd994
        36
    ryd994  
       Jan 22, 2015
    @laoyuan 字符串…………
    假如我的需要精确到小数点后10位
    比较abs(a-b)<c只需要一次减法,一次abs,一次浮点比较
    比较字符串,且不说round和转换的开销,需要整数比较10次(字符就是整数),性能根本没法比
    当然,鉴于是PHP,性能就呵呵无所谓了
    xwsoul
        37
    xwsoul  
       Jan 24, 2015
    为何浮点的梗还在说...这样欺负PHP真的好么? T__T
    laoyuan
        38
    laoyuan  
    OP
       Jan 25, 2015
    @xwsoul 这次主要在说 round 啊
    xwsoul
        39
    xwsoul  
       Jan 25, 2015
    @laoyuan 是时候看看隐式类型转换的原则了
    round 玩来玩去还是浮点啊...
    57.0 == 57 有什么问题么?
    需要类型判断用, 57.0 === 57
    huson
        40
    huson  
       Feb 8, 2015   ❤️ 1
    楼主 没仔细看手册呀,float 那章有解释的
    laoyuan
        41
    laoyuan  
    OP
       Feb 8, 2015
    @huson 重点是 round
    huson
        42
    huson  
       Feb 8, 2015
    @laoyuan
    http://php.net/manual/en/language.types.float.php#warn.float-precision
    这页有讲 栗子在下面


    $x = 8 - 6.4; // which is equal to 1.6
    $y = 1.6;
    var_dump($x == $y); // is not true

    PHP thinks that 1.6 (coming from a difference) is not equal to 1.6. To make it work, use round()

    var_dump(round($x, 2) == round($y, 2)); // this is true

    This happens probably because $x is not really 1.6, but 1.599999.. and var_dump shows it to you as being 1.6.
    laoyuan
        43
    laoyuan  
    OP
       Feb 8, 2015
    @huson 哎呀坑爹啊,我看的中文版手册倒是带评论,但不是按投票排序的,所以评论都略过了!
    bombless
        44
    bombless  
       Feb 22, 2015
    @Kilerd 在C语言中这叫promotion,在此处是不需要区分单精度和双精度的
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   969 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 72ms · UTC 23:40 · PVG 07:40 · LAX 16:40 · JFK 19:40
    ♥ Do have faith in what you're doing.