V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
laoyuan
V2EX  ›  PHP

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

  •  1
     
  •   laoyuan · 2015-01-21 17:06:34 +08:00 · 5544 次点击
    这是一个创建于 3385 天前的主题,其中的信息可能已经有所发展或是发生改变。
    <?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
    第 1 条附言  ·  2015-01-21 18:37:46 +08:00
    其实这个问题是:为什么 round() 返回一个 float,但和 int 比较时总是相等的,别的 float 却没有这个技能
    第 2 条附言  ·  2015-01-21 18:55:30 +08:00
    我大概明白这个问题了:int 和 float 进行比较时,把 int 转为 float 值再比较,而 round() 返回的 float 值恰恰是其对应整数的浮点值,所以 round() 尽管是 float,但和 int 比较时总是相等的。
    44 条回复    2015-02-22 23:49:44 +08:00
    bombless
        1
    bombless  
       2015-01-21 17:37:24 +08:00
    你看,你这就属于学艺不精了……

    我 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  
       2015-01-21 17:37:32 +08:00
    round返回的是浮点啊,被蠢哭
    laoyuan
        3
    laoyuan  
    OP
       2015-01-21 18:01:18 +08:00
    @bombless
    为什么加了 round 的浮点就总是和 int 相等呢,它比普通的浮点高级在哪里??
    tabris17
        4
    tabris17  
       2015-01-21 18:03:19 +08:00
    你看看 $b == $c 的结果,会有惊喜
    laoyuan
        5
    laoyuan  
    OP
       2015-01-21 18:04:25 +08:00
    @eslizn 是啊,返回的是浮点,但进行比较的时候又表现出一副 int 的样子来
    alex321
        6
    alex321  
       2015-01-21 18:05:52 +08:00
    laoyuan
        7
    laoyuan  
    OP
       2015-01-21 18:07:26 +08:00
    @tabris17 没有惊喜,至少没打脸
    bombless
        8
    bombless  
       2015-01-21 18:12:28 +08:00   ❤️ 1
    @laoyuan 不是高级不高级的……

    round之后就是整数了。

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


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

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


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


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

    你需要补课啊 http://zh.wikipedia.org/wiki/IEEE_754
    revlis7
        11
    revlis7  
       2015-01-21 18:14:33 +08:00
    66beta
        12
    66beta  
       2015-01-21 18:16:19 +08:00
    官网手册的示例
    <?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  
       2015-01-21 18:20:43 +08:00
    我想楼主你要没想明白的是这个://(int)('0.57' * 100)=56//
    laoyuan
        14
    laoyuan  
    OP
       2015-01-21 18:36:22 +08:00
    我大概明白这个问题了,int 和 float 进行比较时,把 int 转为 float 值再比较,而 round() 返回的 float 值恰恰是其对应的整数的浮点值,所以 round() 尽管是 float,但和 int 比较时总是相等的。
    FrankFang128
        15
    FrankFang128  
       2015-01-21 18:39:55 +08:00 via Android
    浮点数就不要比较是否相等了
    laoyuan
        16
    laoyuan  
    OP
       2015-01-21 18:43:36 +08:00
    @FrankFang128 不,就用 round 比较,生产环境都是这么做的
    tabris17
        17
    tabris17  
       2015-01-21 18:43:41 +08:00   ❤️ 1
    其实这个问题是:为什么 round() 返回一个 float,但和 int 比较时总是相等的,别的 float 却没有这个技能

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

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

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

    永远不要用`==`

    无论是JS还是PHP

    毕竟他们都是世界上最好的语言
    laoyuan
        22
    laoyuan  
    OP
       2015-01-21 19:01:28 +08:00
    @mcfog 严重同意LS
    Delbert
        23
    Delbert  
       2015-01-21 19:08:33 +08:00
    我试了一下
    $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  
       2015-01-21 19:13:38 +08:00
    @Delbert a 和 c 是整数,b 不是,就这么简单。
    laoyuan
        25
    laoyuan  
    OP
       2015-01-21 19:18:09 +08:00
    @Delbert
    5.7 和 round(0.57 * 10, 1) 是一个数,都是5.7对应的 float 值,而0.57 * 10 这种对 float 进行运算得到的 float,就是一个不可预知的数了,总之 float 的运算很神奇
    bombless
        26
    bombless  
       2015-01-21 19:27:02 +08:00
    噗,我24楼看错了,不好意思。

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

    如果可能的话最好就比较整数,像云风的ejoy游戏引擎为了避开这个问题就把所有的数乘以1024,这样以一定损失为代价,全部都进行整数的比较。
    货币的话常常就直接用十进制表示,不用浮点数了。
    jevonszmx
        27
    jevonszmx  
       2015-01-21 19:37:36 +08:00
    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  
       2015-01-21 20:02:18 +08:00
    看到楼主这样的语言使用者我就明白为什么PHP是世界上最好的语言了
    endrollex
        29
    endrollex  
       2015-01-21 20:40:04 +08:00
    除非你能搞清这个底层是怎么实现的,是不是bitwise comparison,否则返回什么都没错,浮点根本不能用 ==
    Kilerd
        30
    Kilerd  
       2015-01-21 21:38:44 +08:00 via Android
    @bombless double输出要用%lf
    laoyuan
        31
    laoyuan  
    OP
       2015-01-21 22:55:56 +08:00
    @picasso250
    7年PHP经验不是吹的!
    laoyuan
        32
    laoyuan  
    OP
       2015-01-21 22:59:35 +08:00
    @endrollex 浮点根本不能用 ==
    round 之后就能用了啊,虽然 round 得到的也是浮点
    ioth
        33
    ioth  
       2015-01-22 15:12:19 +08:00
    一半语言无所谓,一半比较说不同,不让比较的除外。
    ryd994
        34
    ryd994  
       2015-01-22 15:15:00 +08:00   ❤️ 1
    正常写程序都是不能直接比较浮点的,要比较的话都是 abs(a-b)<某个精确度
    即使是round的结果,如果是浮点数,也无法保证今后就是相等的
    这在任何语言里都是,大多数编程教学里也一定会讲到这一点,你写了7年程序还不知道……
    生产环境里比较round结果……呵呵……
    PHP是世界上最好的语言,可见一斑
    laoyuan
        35
    laoyuan  
    OP
       2015-01-22 15:22:29 +08:00
    @ryd994 那要 round 还有啥用啊,那我转为字符串再比较吧
    ryd994
        36
    ryd994  
       2015-01-22 15:38:14 +08:00
    @laoyuan 字符串…………
    假如我的需要精确到小数点后10位
    比较abs(a-b)<c只需要一次减法,一次abs,一次浮点比较
    比较字符串,且不说round和转换的开销,需要整数比较10次(字符就是整数),性能根本没法比
    当然,鉴于是PHP,性能就呵呵无所谓了
    xwsoul
        37
    xwsoul  
       2015-01-24 23:47:59 +08:00
    为何浮点的梗还在说...这样欺负PHP真的好么? T__T
    laoyuan
        38
    laoyuan  
    OP
       2015-01-25 08:38:59 +08:00
    @xwsoul 这次主要在说 round 啊
    xwsoul
        39
    xwsoul  
       2015-01-25 22:57:10 +08:00
    @laoyuan 是时候看看隐式类型转换的原则了
    round 玩来玩去还是浮点啊...
    57.0 == 57 有什么问题么?
    需要类型判断用, 57.0 === 57
    huson
        40
    huson  
       2015-02-08 18:51:31 +08:00   ❤️ 1
    楼主 没仔细看手册呀,float 那章有解释的
    laoyuan
        41
    laoyuan  
    OP
       2015-02-08 19:07:08 +08:00
    @huson 重点是 round
    huson
        42
    huson  
       2015-02-08 19:20:03 +08:00
    @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
       2015-02-08 19:30:38 +08:00
    @huson 哎呀坑爹啊,我看的中文版手册倒是带评论,但不是按投票排序的,所以评论都略过了!
    bombless
        44
    bombless  
       2015-02-22 23:49:44 +08:00
    @Kilerd 在C语言中这叫promotion,在此处是不需要区分单精度和双精度的
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   1485 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 30ms · UTC 17:08 · PVG 01:08 · LAX 10:08 · JFK 13:08
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.