问题1:除法

在BigInteger提供的除方法Divide只会返回整数,

那如何实现两个BigInteger相除返回0-1的小数呢。

BigInteger的除法的官方回复的方法。

在计算大数值的生命值的进度条时,我们需要一个float的小数表示进度如:1/10 => 0.1f.

1.直接转double计算余数

    public static float DivideToFloat(BigInteger numerator, BigInteger denominator, int decimalPlaces = 2)
    {
        if (denominator == 0)
            return 0;

        // 限制小数位数在合理范围(float精度有限,过多无意义)
        decimalPlaces = Math.Clamp(decimalPlaces, 0, 6); // float有效数字约7位

        // 计算整数部分
        BigInteger quotient = BigInteger.DivRem(numerator, denominator, out BigInteger remainder);

        // 无余数或不需要小数部分
        if (remainder == 0 || decimalPlaces == 0)
        {
            // 处理超出float范围的情况
            if (quotient > (BigInteger)float.MaxValue)
                return float.MaxValue;
            if (quotient < (BigInteger)float.MinValue)
                return float.MinValue;
            return (float)quotient;
        }

        // 计算小数部分(通过放大余数计算)
        double fractionalPart = 0;
        BigInteger scaledRemainder = remainder;
        double divisor = (double)denominator; 

        for (int i = 0; i < decimalPlaces; i++)
        {
            scaledRemainder *= 10;
            BigInteger digit = scaledRemainder / denominator;
            fractionalPart = fractionalPart * 10 + (double)digit;
            scaledRemainder %= denominator;

        }

        // 缩放小数部分到正确的数量级(例如2位小数除以100)
        fractionalPart /= Math.Pow(10, decimalPlaces);

        // 合并整数和小数部分
        double result = (double)quotient + fractionalPart;

        // 限制在float的取值范围内
        if (result > float.MaxValue)
            return float.MaxValue;
        if (result < float.MinValue)
            return float.MinValue;

        return (float)result;
    }

2.转字符串进行位运算

public static string DivideToString(BigInteger numerator, BigInteger denominator, int decimalPlaces = 2)
    {
        if (denominator == 0)
            return 0;

        // 限制最大小数位数,防止恶意输入导致性能问题
        decimalPlaces = Math.Clamp(decimalPlaces, 0, 1000);

        // 计算整数部分和余数
        BigInteger quotient = BigInteger.DivRem(numerator, denominator, out BigInteger remainder);

        // 无余数,直接返回整数
        if (remainder == 0 || decimalPlaces == 0)
            return quotient;

        // 使用StringBuilder高效拼接字符串
        var decimalPart = new StringBuilder(decimalPlaces);
        BigInteger scaledRemainder = remainder;
        bool hasRemainingDigits = true;

        for (int i = 0; i < decimalPlaces && hasRemainingDigits; i++)
        {
            // 放大余数(乘以10)
            scaledRemainder *= 10;

            // 计算当前位的数字
            BigInteger digit = scaledRemainder / denominator;
            decimalPart.Append(digit.ToString());

            // 更新余数(取模)
            scaledRemainder %= denominator;

            // 如果余数为0,后续都是0,无需继续计算
            if (scaledRemainder == 0)
            {
                hasRemainingDigits = false;
                // 补0到指定小数位数(如果需要)
                if (i + 1 < decimalPlaces)
                {
                    decimalPart.Append('0', decimalPlaces - (i + 1));
                }
            }
        }
        return $"{quotient}.{decimalPart}";
    }

问题2:乘法

BigInteger的乘法如何实现乘float的数 如:0.8f

思路:BigInteger可以先乘以new BigInteger(8)再除以Divide new BigInteger(10)

方法一:在确定float的位数较小时可以直接固定分子分母的大小如:100

    /// <summary>
    /// bigint小数乘法,
    /// 小数固定2位
    /// </summary>
    /// <param name="bigInt"></param>
    /// <param name="fl"></param>
    /// <returns></returns>
    public static BigInteger BigIntMultiply(BigInteger bigInt, float fl)
    {
        var numerator = new BigInteger(fl * 100);
        return bigInt * numerator / new BigInteger(100);
    }

方法二:如果float的位数都不满足时可以参考下面的方法使用decimal的128位的类型

    // 将decimal转换为分数形式(分子和分母)
    public static (BigInteger numerator, BigInteger denominator) ToFraction(decimal d)
    {
        // 获取decimal的内部表示(4个32位整数)
        int[] bits = decimal.GetBits(d);

        // 计算符号:如果第3个整数的第31位为1,则为负数
        int sign = (bits[3] >> 31) & 1;
        BigInteger signFactor = sign == 1 ? -1 : 1;

        // 计算分子(合并低3个整数的位)
        BigInteger numerator = signFactor * unchecked(
            ((BigInteger)(uint)bits[2] << 64) |  // 最高32位
            ((BigInteger)(uint)bits[1] << 32) |  // 中间32位
             (BigInteger)(uint)bits[0]           // 最低32位
        );

        // 计算分母(10的幂,由小数位数决定)
        int exponent = (bits[3] >> 16) & 0xFF;  // 获取小数位数(0-28)
        BigInteger denominator = BigInteger.Pow(10, exponent);

        return (numerator, denominator);
    }

    // 将BigInteger与decimal相乘
    public static BigInteger Multiply(BigInteger bigInt, decimal dec)
    {
        // 将decimal转换为分数
        (BigInteger numerator, BigInteger denominator) = ToFraction(dec);

        // 计算:(BigInteger * 分子) / 分母
        return (bigInt * numerator) / denominator;
    }

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐