浮点数有时候很讨厌,因为它有误差:

一般这并不重要,但是有些情况下会产生一些bug。比如:

这是一个平台跳跃游戏,玩家在地块上移动。假想我希望右边的地块与左边地块高度相同,完美拼接,玩家可以直接走过去而不用跳跃。但是因为一些浮点数计算顺序的原因,右边的地块比左边的地块高了一点点,比如 0.00000000000000004,那么玩家就可能会在这里卡住。当然,这个 bug 不难解决,比如可以让玩家遇到很小的坎时自动爬上去,或者直接把玩家碰撞箱改成胶囊。
但并不是每一个由浮点数误差导致的 bug 都那么容易解决,尤其在你的整个游戏系统就是离散的,比如建立在某种网格上的时候。这种情况下一般来说会直接使用整数,但也有时候你就是需要(或者策划就是需要)一些 0.5 这样的半整数,而你又不想为了这个 0.5 动用麻烦的浮点数。你可以选择用一个整数来存储这个 0.5 的两倍。这不错,但是说实话不太优雅,而且你容易写着写着就忘了这件事,然后某个地方忘了乘以二,于是出 bug。这虽然都是小 bug,但是很容易把你的脑子搞得很乱。(显然我的脑子就被这种事情搞乱过。)而且,你也不知道哪天策划又突然提出来要 0.1 或者 0.25。
一个比较好的解决方案是,我们可以搞一个大分数类。大分数依赖于大整数,因此不用担心数值溢出之类的问题,同时也没有浮点数那样的误差。你可以大大方方地写 x == 0.5 而不是 Mathf.abs(x - 0.5) < 0.001。
隔壁 java 的 Apache Commons 有一个 BigFraction 类。然而如果你使用 Unity 的话,C# 只自带 BigInteger,没有大分数。因为我可能早晚要用到,所以决定直接写一个:
https://github.com/AC-Mnky/C-Sharp-Frac
写了测试脚本并跑通了,希望没有神秘bug。
值得一提的是,大整数的性能其实比较差,大分数就自然还要更差一点。不过大部分游戏的大部分性能消耗都在渲染和交互上。核心逻辑计算部分用大分数,除非有特殊算法需求,否则一般都不会导致性能问题。主要还是看愿不愿意引入奇怪的轮子吧。
--------2026.2.22更新--------
加入了更多功能。除了一些方便的功能(比如 int 的自动类型转换),重点是加入了 Unity 的序列化和反序列化方法,以及自定义的 Inspector UI。


暂无关于此日志的评论。