C#生成随机数计算出现负数结果的原因排查
问题:随机数计算偶尔出现负数的原因?
我尝试生成20个1-100之间的随机数,通过声明委托(Delegate)和事件(Event)实现逻辑:若存在大于50的数则将所有数相乘,否则求和。代码如下:
using System; using System.Linq; namespace ConsoleApp { class Program { // Delegate tanımla public delegate int MyDelegate(int[] arr); public event MyDelegate OnResultCalculated; static void Main(string[] args) { Program p = new Program(); // 1-100 arasında 20 adet rastgele sayı üret Random rnd = new Random(); int[] numbers = Enumerable.Range(1, 100).OrderBy(x => rnd.Next()).Take(20).ToArray(); // Eğer 50'den büyük sayı varsa çarp, yoksa topla int result = numbers.Any(x => x > 50)? p.Multiply(numbers) : p.Add(numbers); // Event'i tetikle p.OnResultCalculated?.Invoke(numbers); Console.WriteLine(result); Console.ReadLine(); } public int Multiply(int[] arr) { return arr.Aggregate(1, (a, b) => a * b); } public int Add(int[] arr) { return arr.Sum(); } } }
代码可正常运行,但偶尔会得到负数结果,请问问题出在哪里?
解答
问题的核心是整数溢出:
- C# 中
int类型的取值范围是-2^31到2^31-1(即-2147483648到2147483647)。 - 当计算20个1-100之间的数相乘时,哪怕是较小的数(比如连续20个5相乘:
5^20=95367431640625),结果也远远超出了int的最大值。 - .NET 默认在unchecked模式下运行,整数溢出不会抛出异常,而是按照二进制补码规则循环计算,最终得到负数结果。
修复方案
方案1:改用更大范围的数值类型
把委托、Multiply方法的返回值类型改成long(取值范围 -9223372036854775808 到 9223372036854775807),能容纳更大的乘积:
// 修改委托类型 public delegate long MyDelegate(int[] arr); public event MyDelegate OnResultCalculated; // 修改Multiply方法 public long Multiply(int[] arr) { return arr.Aggregate(1L, (a, b) => a * b); } // 主方法中接收结果的变量也改成long long result = numbers.Any(x => x > 50) ? p.Multiply(numbers) : p.Add(numbers);
方案2:使用BigInteger处理超大数值
如果乘积可能超出long的范围(比如20个100相乘:100^20=10^40,远大于long的最大值),可以用System.Numerics.BigInteger类型:
首先添加对System.Numerics程序集的引用,然后修改代码:
using System.Numerics; // 修改委托类型 public delegate BigInteger MyDelegate(int[] arr); public event MyDelegate OnResultCalculated; // 修改Multiply方法 public BigInteger Multiply(int[] arr) { return arr.Aggregate(BigInteger.One, (a, b) => a * b); } // 主方法中接收结果的变量改成BigInteger BigInteger result = numbers.Any(x => x > 50) ? p.Multiply(numbers) : new BigInteger(p.Add(numbers));
方案3:启用溢出检查(可选)
如果希望溢出时抛出异常而非静默产生错误结果,可以在代码中使用checked块:
public int Multiply(int[] arr) { return checked(arr.Aggregate(1, (a, b) => a * b)); }
这样当溢出发生时会抛出OverflowException,方便及时发现问题,但不会解决结果负数的问题,只是提前暴露错误。
内容的提问来源于stack exchange,提问作者Megakko
相关产品推荐
相关产品推荐

