hzhh95963

蓝猫淘气三千问:为什么舰C里面不使用四舍五入?

[i=s] 本帖最后由 hzhh95963 于 2015-3-10 00:05 编辑

看到不少提督在计算火力和伤害等数值的时候,在无意中使用了四舍五入的取整方式。但是事实上,舰C的取整全部是向下取整(或向零取整,后面会提)这是为何呢?

那么我们来看一看计算机是如何处理取整这个操作的。常见的C语言来实现取整:

[code]// C sytle

float foo = 10.6;

int bar = (int)foo; // bar == 10[/code]或

[code]// C sytle

float foo = 10.6;

int bar = int(foo); // bar == 10[/code]

或者是在C++更加推荐的方式:

[code]// C++ sytle

float foo = 10.6;

int bar = static_cast(foo); // bar == 10[/code]

这里面要仔细追究也是说来话长。为什么C/C++没有采用向最接近整数取整而是向零取整?

这个问题其实很尴尬……在C99以前,C语言的标准都没有明确规定到底应该怎么取整,这个问题被留给了编译器自己来决定。编译器选择的是向Fortran靠拢。(后来在C99,向零取整就变成了语言标准)。那么Fortran为什么又要那么取整呢?具体的原因我没有调查到。这其中应该有数学上的考量,也有对当时的硬件环境的考虑。毕竟Fortran诞生的时候,就连IEEE 754这个浮点数标准都还没有出现呢。然后C++为了兼容C语言,继承了这个设计……接下来Java这个C++的精神继任者也继续采用了这个设计……

不过舰娘的后台服务端一般不会是用C/C++来写的。那些动态语言们往往有其他的实现方式。

比如,我来看看用世界上最好的编程语言PHP怎么来实现这个[s](才不是)[/s]

[code] $foo = 10.6;

$bar = floor($foo); // $bar == 10

$bar = round($foo); // $bar == 11

$bar = intval($foo) // $bar == 10

?>[/code]floor和intval有什么区别?floor返回的是一个浮点型,intval返回的是一个整型,round和ceil返回的居然也是浮点型……

[s]我个人认为PHP的类型转换简直是个死亡雷区……[/s]

那么不要管PHP了,日本人发明的Ruby是什么情况呢?

[code]# Ruby

foo = 10.6

bar = foo.floor # bar == 10

bar = foo.round # bar == 11[/code]

嗯……这两个都很正常地返回了一个整型……

说了半天,舰C是什么情况?舰C似乎并没有像前面的动态语言那样有过四舍五入?我个人认为,舰C作为一个使用HTTP连接的页游,后台非常有可能是用Java写的:

[code]// Java

foo = 10.6;

int bar = (int)foo; // bar == 10[/code](好像有点眼熟)

看,是不是都挺简单的?那么如果要使用四舍五入规则呢?事实上,一直到C99和C++11之前,C/C++都没有一个round函数。好吧,我就只写一个C++版本的吧。

[code]// C++ sytle

float foo = 10.6;

int bar;

int floor = static_cast(foo);

if ((foo – floor) < 0.5 || (foo - floor) > -0.5) {

bar = floor;

} else {

bar = floor + ((foo > 0) – (foo < 0)); // bar == 11
}[/code]感觉效率不是很高的样子……

不过Java就更加残念了……它标准库里自带的Math.round的实现是错误的……把-10.5传给这个函数返回的结果是-10而不是-11……

另外还有一些更复杂的数值修约规则,比如“奇进偶舍”……要写的代码肯定就是更长了……除非采取某些特别tricky的办法,具体不再展开了……

那么,现在假设你是程序员来实现舰娘的服务器,你会选择哪种方式呢?

一些题外话

事实上,常用的编程语言有两种取整,一种是“向零取整”,向0方向取最接近的整数。C、C++、Java采用的就是这种模式。另一种是“向下取整”,向-∞方向取最接近的整数,Python、Ruby就是采用的这种模式。两者在正数上是一致的,区别就在于对负数取整的不同行为。这里就不再展开了。编程语言具体采用哪种方式都有诸多的原因。不过无论用哪种方式,都有大坑在等着……

发表回复

  1. qhdasd说道:

    围观技术贴

  2. xiannuan说道:

    看大神科普

  3. 幻星说道:

    我就围观一下_(:з」∠)_

  4. {:4_114:}完全不懂了

  5. Aris说道:

    围观支持,但是完全看不懂{:4_88:}

  6. 驱逐舰夕立说道:

    你说这个谁懂啊_(:зゝ∠)_

  7. 朝岚暮凪说道:

    都是代码,我只看结果

  8. hzhh95963说道:

    朝岚暮凪 发表于 2015-3-9 23:55

    都是代码,我只看结果

    {:4_102:}水贴还要看什么结果,好好灌水

  9. Hawke0323说道:

    简单的说,向下取整什么的,切了就行了

    四舍五入切了之后弄不好还要装个什么上去,太麻烦

  10. 雾雨爱丽丝说道:

    虽不明但觉厉……

  11. chaim说道:

    谢科普[s]虽然看不懂[/s]

    那么我有一个问题,装甲乱数的结果也是向下取整的?也就是说实际的最低数值会比2/3更低?还是说不是3的倍数的装甲值不会取到2/3?

  12. EMPTY说道:

    {:4_102:}这算围观程序猿么

  13. hzhh95963说道:

    chaim 发表于 2015-3-9 23:59

    谢科普[s]虽然看不懂[/s]

    那么我有一个问题,装甲乱数的结果也是向下取整的?也就是说实际的最低数值会比2/ …

    这个其实倒未必……装甲有可能是保留了小数点,一直到最后算伤害的时候再取整……

  14. chaim说道:

    hzhh95963 发表于 2015-3-10 00:03

    这个其实倒未必……装甲有可能是保留了小数点,一直到最后算伤害的时候再取整…… …

    确实也有这种可能,3Q!

  15. syd09htyk说道:

    我其实挺怀疑角川程序猿不是故意用int指令而是在各种除法的时候用了整除…Java和ruby都是明确写明正整数除法是向下取整的(java是toward 0,ruby是toward infinity)

  16. punio600说道:

    修正向下取整为四舍五入的方法再简单不过了——将基数全部加0.5

    简单快捷,一行代码

  17. hzhh95963说道:

    syd09htyk 发表于 2015-3-10 00:12

    我其实挺怀疑角川程序猿不是故意用int指令而是在各种除法的时候用了整除…Java和ruby都是明确写明正整数除 …

    Python也是向下取整,我曾经被这个坑过……不过这些动态语言都设计得挺好,Python也有round函数,所以在使用的方便程度上四舍五入倒是和向下取整是差不多的。

    舰C有些地方看起来不像是用到了除法,比如基础火力的计算,在那个取整之前是一串浮点数的乘法

  18. xiannuan说道:

    punio600 发表于 2015-3-10 00:17

    修正向下取整为四舍五入的方法再简单不过了——将基数全部加0.5

    简单快捷,一行代码 …

    {:4_114:}好像真的可以

  19. hzhh95963说道:

    [i=s] 本帖最后由 hzhh95963 于 2015-3-10 00:22 编辑

    punio600 发表于 2015-3-10 00:17

    修正向下取整为四舍五入的方法再简单不过了——将基数全部加0.5

    简单快捷,一行代码 …

    你把负数作为参数放进去算算看是什么结果……

    Java就是用了这种偷懒的做法……

  20. 幻想の自由说道:

    _(:з」∠)_..完全不明觉厉的看大神

  21. Pat说道:

    syd09htyk 发表于 2015-3-10 00:12

    我其实挺怀疑角川程序猿不是故意用int指令而是在各种除法的时候用了整除…Java和ruby都是明确写明正整数除 …

    还有sqrt呢,应该还是用了int的

  22. syd09htyk说道:

    hzhh95963 发表于 2015-3-10 00:20

    Python也是向下取整,我曾经被这个坑过……不过这些动态语言都设计得挺好,Python也有round函数,所以在 …

    基础火力那边如果实际码是*乱数/3这样就能[s]强行[/s]解释了

    我有这个想法是因为有无聊人去集过装甲乱数的出现频率结果是一个奇怪的分开数十个区间的N面骰模型而不是正常的各整数数值出现频率基本相等…

  23. punio600说道:

    hzhh95963 发表于 2015-3-10 00:21

    你把负数作为参数放进去算算看是什么结果……

    Java就是用了这种偷懒的做法…… …

    嘛,我们就假设舰C在计算伤害直线先要来个判定决定是否擦弹吧…

    所以负数根本就不考虑了(强行解释)

  24. syd09htyk说道:

    hzhh95963 发表于 2015-3-10 00:21

    你把负数作为参数放进去算算看是什么结果……

    Java就是用了这种偷懒的做法…… …

    别黑java了人家不是亡羊补牢在手册出了个toward0了么{:4_114:}

  25. hzhh95963说道:

    syd09htyk 发表于 2015-3-10 00:31

    基础火力那边如果实际码是*乱数/3这样就能[s]强行[/s]解释了

    我有这个想法是因为有无聊人去集过装甲乱数 …

    其实也有可能是舰C的随机算法做得太烂……(好像很有可能……)

  26. 駆逐艦雪風说道:

    _(:зゝ∠)_不明觉厉,只能看懂一小部分基本的

  27. 影月圓舞说道:

    萌新默默圍觀dalao發言

  28. hzhh95963说道:

    syd09htyk 发表于 2015-3-10 00:34

    别黑java了人家不是亡羊补牢在手册出了个toward0了么

    {:4_114:}但是标准库的实现还是错的……

  29. Pat说道:

    hzhh95963 发表于 2015-3-10 00:21

    你把负数作为参数放进去算算看是什么结果……

    Java就是用了这种偷懒的做法…… …

    对于C++这种rounding towards zero的好像可以用这种方式达到rounding half away from zero?

    [code]// C++

    double foo = -10.5;

    int floor = (int) (foo + (foo >= 0) – 0.5);[/code]

官方微信

Login

跳至工具栏