当前位置: 代码迷 >> Java相关 >> 说说溢算那些事~您的计算方式溢算了吗
  详细解决方案

说说溢算那些事~您的计算方式溢算了吗

热度:44   发布时间:2016-04-22 20:08:59.0
说说溢算那些事~!你的计算方式溢算了吗?

游戏快正式上线了,今天发现一个bug,让人哭笑不得。数据计算溢出了;玩家充值的元宝变为了0;这个可是一件大事,毕竟谁都担不起这个责任啊;

来说说原因吧。开发语言是 java 工具是 netbeans ide 8.0.2

玩家对象有一个属性是 gold 是int类型的;

玩家充值的时候计算方式如下.

        int gold = 20000;//玩家原有的        int tempGold = 20000;//玩家现在充值的        if (Integer.MAX_VALUE >= gold + tempGold) {            gold = gold + tempGold;        } else {            gold = Integer.MAX_VALUE;        }

看上去好像没什么问题是吧。当然以上是模拟的;

如果你经验丰富的话,或以下看出一些端倪,那就是会溢算的;

也许可能你看不出什么问题,我刚开始也没发现什么问题,所以代码就这么写了,那好,我们来模拟一下

        int gold = Integer.MAX_VALUE - 1800;//玩家原有的        int tempGold = 20000;//玩家现在充值的        if (Integer.MAX_VALUE >= gold + tempGold) {            gold = gold + tempGold;            System.out.println("1");        } else {            gold = Integer.MAX_VALUE;            System.out.println("2");        }

 

好大家猜一猜,这是会输出什么结果????

也许你会回答输出 2 对。没错我也以为会输出 2 ,

可是运行结果为什么是 1 呢?

首先我们分析一下,为什么我们以为会输出 2 ?那么很明显我们把

gold + tempGold

这两个值的计算想当然的以为会变成 long 型 而大于 Integer.MAX_VALUE

然而事实并非这样,我来看看输出结果

        int gold = Integer.MAX_VALUE - 1800;//玩家原有的        int tempGold = 20000;//玩家现在充值的        if (Integer.MAX_VALUE >= gold + tempGold) {            gold = gold + tempGold;            System.out.println("1");        } else {            gold = Integer.MAX_VALUE;            System.out.println("2");        }        System.out.println(gold + tempGold);

 

--- exec-maven-plugin:1.2.1:exec (default-cli) @ game-gamesr ---1-2147445449------------------------------------------------------------------------BUILD SUCCESS

 

在java的机制下 gold + tempGold 相加并非变成 long 型而是负数

看到这里,也许你会嘲笑我,好吧我承认,我确实没有验证过这个问题。好在游戏还没有上线。测试发现问题。

不管这样,现在我发现了这个问题,并且了解到了问题所在。好吧想办法解决呗。

也就是把 int 转变为 long 的问题

        int gold = Integer.MAX_VALUE - 1800;//玩家原有的        int tempGold = 20000;//玩家现在充值的        long tempLGold = tempGold;        if (Integer.MAX_VALUE >= gold + tempLGold) {            gold = gold + tempGold;            System.out.println("1");        } else {            gold = Integer.MAX_VALUE;            System.out.println("2");        }        System.out.println(gold + tempGold);

测试一下现在的输出结果呢?

--- exec-maven-plugin:1.2.1:exec (default-cli) @ game-gamesr ---2-2147463649------------------------------------------------------------------------BUILD SUCCESS

这些正确了,,好吧。。犯二的事情结束了。可是发现这里多了一个变量 long tempLGold;属性和操作不是很方便,还有没有更好的操作;

        int gold = Integer.MAX_VALUE - 1800;//玩家原有的        int tempGold = 20000;//玩家现在充值的        if (Integer.MAX_VALUE >= gold + tempGold + 0L) {            gold = gold + tempGold;            System.out.println("1");        } else {            gold = Integer.MAX_VALUE;            System.out.println("2");        }

注意后面那个0L 

看看输出

--- exec-maven-plugin:1.2.1:exec (default-cli) @ game-gamesr ---1------------------------------------------------------------------------BUILD SUCCESS

结果还是输出1,也许你会嘲笑我有范二了,对我确实犯二 了,,深究才知道 运算符优先级问题。

那好吧再改改

        int gold = Integer.MAX_VALUE - 1800;//玩家原有的        int tempGold = 20000;//玩家现在充值的        if (Integer.MAX_VALUE >= 0L + gold + tempGold) {            gold = gold + tempGold;            System.out.println("1");        } else {            gold = Integer.MAX_VALUE;            System.out.println("2");        }

输出

--- exec-maven-plugin:1.2.1:exec (default-cli) @ game-gamesr ---2------------------------------------------------------------------------BUILD SUCCESS

还可以

        int gold = Integer.MAX_VALUE - 1800;//玩家原有的        int tempGold = 20000;//玩家现在充值的        if (Integer.MAX_VALUE >= gold + tempGold * 1L) {            gold = gold + tempGold;            System.out.println("1");        } else {            gold = Integer.MAX_VALUE;            System.out.println("2");        }
--- exec-maven-plugin:1.2.1:exec (default-cli) @ game-gamesr ---2------------------------------------------------------------------------BUILD SUCCESS

这下正确的了,,,

 

失足程序员的犯二事情啊。。。

 

1楼Grid Science
四件事。,1、装/拆箱表示的是基础类型与类类型的转换,int、long都是基础类型,转换时不会装拆箱。再看int和Integer,前者是基础类型,后者是类(实例化后产生对象),转换时要装箱和拆箱。(吐槽:比C#中int与Int32的转换要麻烦多了——C#中int就是System.Int32结构的别名,不会装拆箱。),2、差减法和累加法最大的不同在于表达式的有效范围。考虑伪代码:if (0x7FFFFFEE + val gt; 0x7FFFFFFF) 而且已知所有数据类型都是32位有符号整数,那么这个判断很容易就产生溢出。相比之下,还不如写成 if (0x7FFFFFFF - 0x7FFFFFEE lt; val),该判断语句的安全范围比上一句大得多,只要val是整数即可。(考虑到实际情况,也可以设为非负数。)文中的情况也是一样的。另外,精度有时也要考虑,如两个相差不大(例如0.0001)的浮点数作差就是很糟糕的事情,因为精度限制,作差的结果会不准,此时就应该用前一种的风格的判断,用val来增加有效范围。——建议看一下“计算方法”这个方面的书,3、BigDecimal是处理整数运算的;在计算范围很大的同时也带来了效率的损失。(记得常见的实现方式,如 GMP,是基于字符串的。)既然是网游,而且设计上只用整数而没有使用浮点数,就没必要用BigDecimal。,4、C#中默认开启了溢出检查(checked
  相关解决方案