Unity中如何将BigInteger转换为带两位小数的浮点数?BigDecimal库不可用
解决方案:Unity点击器游戏BigInteger货币的两位小数格式化问题
核心问题分析
你原代码的关键问题是BigInteger转decimal时会溢出——decimal类型有最大范围限制(约7.9×10²⁸),而你的除数数组里包含10⁵²这种超大值,当货币数值超过decimal上限时,强制转换会直接抛出异常。同时,直接用浮点数处理还可能带来精度丢失的问题。
下面给出两种实用解决方案,全程用整数运算规避这些问题:
方案1:用最小面额整数存储(推荐)
点击器游戏最稳妥的做法是把货币单位设为最小面额(比如“分”),用BigInteger存储总分数,所有经济计算都用整数,彻底避免小数精度问题。格式化时再转换为带两位小数的金额:
// totalCents是存储的总分数,例如1234代表12.34元 public string FormatMoney(BigInteger totalCents) { // 拆分元和分 BigInteger dollars = totalCents / 100; int cents = (int)(totalCents % 100); // 小额直接显示 if (dollars < 1000) { return $"{dollars}.{cents:D2}"; } BigInteger[] divisors = { BigInteger.Pow(10, 3), BigInteger.Pow(10, 6), BigInteger.Pow(10, 9), BigInteger.Pow(10, 12), BigInteger.Pow(10, 15), BigInteger.Pow(10, 18), BigInteger.Pow(10, 21), BigInteger.Pow(10, 24), BigInteger.Pow(10, 27), BigInteger.Pow(10, 30), BigInteger.Pow(10, 33), BigInteger.Pow(10, 36), BigInteger.Pow(10, 39), BigInteger.Pow(10, 42), BigInteger.Pow(10, 45), BigInteger.Pow(10, 48), BigInteger.Pow(10, 52) }; string[] suffixes = {"K", "M", "B", "T", "aa", "ab", "ac", "ad", "ae", "af", "ag", "ah", "ai", "aj", "ak", "al" }; // 匹配对应的后缀 for (int i = 0; i < divisors.Length; i++) { if (dollars < divisors[i] * 1000) { // 用整数运算计算带两位小数的结果,避免溢出 BigInteger scaled = dollars * 100 / divisors[i]; string wholePart = (scaled / 100).ToString(); string decimalPart = (scaled % 100).ToString("D2"); return $"{wholePart}.{decimalPart}{suffixes[i]}"; } } // 处理最大数值 int lastIndex = divisors.Length - 1; BigInteger finalScaled = dollars * 100 / divisors[lastIndex]; string finalWhole = (finalScaled / 100).ToString(); string finalDecimal = (finalScaled % 100).ToString("D2"); return $"{finalWhole}.{finalDecimal}{suffixes[lastIndex]}"; }
这种方式从根源上解决了小数存储的精度问题,商品定价直接用分数(比如1234就是12.34元),所有运算都是整数,不会出现精度丢失或溢出。
方案2:直接格式化BigInteger为带两位小数的字符串(针对已用元存储的场景)
如果已经用BigInteger存储的是元(比如12代表12.00元),需要显示带两位小数的格式,可通过纯整数运算处理,避免浮点数转换:
public string FormatMoney(BigInteger value) { // 小额直接显示两位小数 if (value < 1000) { return $"{value}.00"; } BigInteger[] divisors = { BigInteger.Pow(10, 3), BigInteger.Pow(10, 6), BigInteger.Pow(10, 9), BigInteger.Pow(10, 12), BigInteger.Pow(10, 15), BigInteger.Pow(10, 18), BigInteger.Pow(10, 21), BigInteger.Pow(10, 24), BigInteger.Pow(10, 27), BigInteger.Pow(10, 30), BigInteger.Pow(10, 33), BigInteger.Pow(10, 36), BigInteger.Pow(10, 39), BigInteger.Pow(10, 42), BigInteger.Pow(10, 45), BigInteger.Pow(10, 48), BigInteger.Pow(10, 52) }; string[] suffixes = {"K", "M", "B", "T", "aa", "ab", "ac", "ad", "ae", "af", "ag", "ah", "ai", "aj", "ak", "al" }; // 匹配对应的后缀 for (int i = 0; i < divisors.Length; i++) { if (value < divisors[i] * 1000) { // 把保留两位小数的计算转为整数运算 BigInteger scaled = value * 100 / divisors[i]; string wholePart = (scaled / 100).ToString(); string decimalPart = (scaled % 100).ToString("D2"); return $"{wholePart}.{decimalPart}{suffixes[i]}"; } } // 处理最大数值 int lastIndex = divisors.Length - 1; BigInteger finalScaled = value * 100 / divisors[lastIndex]; string finalWhole = (finalScaled / 100).ToString(); string finalDecimal = (finalScaled % 100).ToString("D2"); return $"{finalWhole}.{finalDecimal}{suffixes[lastIndex]}"; }
这个方案通过value * 100 / divisors[i]将两位小数的计算转为纯整数操作,完全规避了浮点数的精度和溢出问题,Unity中可以直接运行。
内容的提问来源于stack exchange,提问作者Mister Danila
相关产品推荐
相关产品推荐

