C#中的大数值BigInteger的乘除改进
·
问题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;
}
更多推荐



所有评论(0)