C#
random decimal
programming
.NET
code example

Generating a random decimal in C

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

C# provides several ways to generate random decimal numbers. Random.NextDouble() returns a double between 0.0 and 1.0, which you can scale to any range. For the decimal type specifically (needed in financial calculations), there is no built-in NextDecimal() method, so you build one from random bytes or by combining random integers. For cryptographically secure random numbers, use RandomNumberGenerator instead of System.Random.

Random Double (0.0 to 1.0)

csharp
1var random = new Random();
2
3// Returns a double >= 0.0 and < 1.0
4double value = random.NextDouble();
5Console.WriteLine(value); // e.g., 0.7345812...

Random Double in a Custom Range

csharp
1var random = new Random();
2
3// Random double between min and max
4double RandomInRange(double min, double max)
5{
6    return min + (random.NextDouble() * (max - min));
7}
8
9double price = RandomInRange(9.99, 49.99);
10Console.WriteLine($"Price: {price:F2}"); // e.g., Price: 27.43
11
12double temperature = RandomInRange(-20.0, 45.0);
13Console.WriteLine($"Temp: {temperature:F1}°C"); // e.g., Temp: 12.3°C

The formula min + (NextDouble() * (max - min)) generates a uniform random value in [min, max).

Random Float

csharp
1var random = new Random();
2
3// .NET 6+ has Random.NextSingle()
4float value = random.NextSingle(); // 0.0f to 1.0f
5
6// For older .NET versions, cast from double
7float value2 = (float)random.NextDouble();
8
9// Float in range
10float RandomFloat(float min, float max)
11{
12    return min + (random.NextSingle() * (max - min));
13}

Random Decimal

System.Random has no NextDecimal() method. Build one from random components:

Method 1: Scale from Double

csharp
1var random = new Random();
2
3// Simple but limited to double's 15-16 digit precision
4decimal RandomDecimal(decimal min, decimal max)
5{
6    double range = (double)(max - min);
7    return min + (decimal)(random.NextDouble() * range);
8}
9
10decimal amount = RandomDecimal(0.01m, 999.99m);
11Console.WriteLine($"Amount: {amount:C}"); // e.g., Amount: $542.17

Method 2: From Random Bytes (Full Precision)

csharp
1decimal NextDecimal(Random random)
2{
3    // decimal has 96-bit integer + scale (0-28) + sign
4    int lo = random.Next();
5    int mid = random.Next();
6    int hi = random.Next();
7    bool isNegative = random.Next(2) == 1;
8    byte scale = (byte)random.Next(29); // 0 to 28
9
10    return new decimal(lo, mid, hi, isNegative, scale);
11}
12
13var random = new Random();
14decimal d = NextDecimal(random);
15Console.WriteLine(d);

Method 3: Decimal in Range with Precision

csharp
1decimal RandomDecimalInRange(Random random, decimal min, decimal max, int decimalPlaces = 2)
2{
3    double range = (double)(max - min);
4    decimal value = min + (decimal)(random.NextDouble() * range);
5    return Math.Round(value, decimalPlaces);
6}
7
8var random = new Random();
9decimal price = RandomDecimalInRange(random, 1.00m, 100.00m, 2);
10Console.WriteLine($"${price}"); // e.g., $47.83

Cryptographically Secure Random

System.Random is a pseudorandom number generator (PRNG) — predictable given the seed. For security-sensitive applications, use RandomNumberGenerator:

csharp
1using System.Security.Cryptography;
2
3// .NET 6+ simplified API
4double secureDouble = RandomNumberGenerator.GetInt32(int.MaxValue) / (double)int.MaxValue;
5
6// Full method for secure random double
7double SecureRandomDouble()
8{
9    byte[] bytes = new byte[8];
10    RandomNumberGenerator.Fill(bytes);
11    ulong value = BitConverter.ToUInt64(bytes, 0);
12    return (double)value / ulong.MaxValue;
13}
14
15double secureValue = SecureRandomDouble();
16Console.WriteLine(secureValue);
csharp
1// Secure random in range
2double SecureRandomInRange(double min, double max)
3{
4    return min + SecureRandomDouble() * (max - min);
5}

Thread Safety

System.Random is not thread-safe. In multi-threaded code:

csharp
1// WRONG — shared Random instance causes duplicate values or exceptions
2static Random shared = new Random();
3
4// Option 1: .NET 6+ shared thread-safe instance
5double value = Random.Shared.NextDouble();
6
7// Option 2: ThreadLocal for older .NET
8static readonly ThreadLocal<Random> threadRandom =
9    new(() => new Random(Guid.NewGuid().GetHashCode()));
10
11double threadSafeValue = threadRandom.Value.NextDouble();
12
13// Option 3: Lock (slower)
14static readonly Random lockedRandom = new Random();
15static readonly object lockObj = new();
16
17double LockedNextDouble()
18{
19    lock (lockObj) { return lockedRandom.NextDouble(); }
20}

Generating Random Decimal Arrays

csharp
1var random = new Random();
2
3// Array of random doubles
4double[] prices = Enumerable.Range(0, 100)
5    .Select(_ => Math.Round(random.NextDouble() * 100, 2))
6    .ToArray();
7
8// Array of random decimals in a range
9decimal[] amounts = Enumerable.Range(0, 50)
10    .Select(_ => Math.Round((decimal)(random.NextDouble() * 1000), 2))
11    .ToArray();

Common Pitfalls

  • Creating new Random() instances in a tight loop: new Random() seeds from the system clock. Multiple instances created within the same millisecond produce identical sequences. Use a single instance or Random.Shared (.NET 6+).
  • Assuming NextDouble() can return 1.0: NextDouble() returns values in [0.0, 1.0) — the upper bound is exclusive. The maximum value is 0.99999999999999978. If you need an inclusive upper bound, add a small epsilon to the range.
  • Using Random for security-sensitive purposes: System.Random is predictable if the seed is known. Use RandomNumberGenerator from System.Security.Cryptography for tokens, passwords, or any security-related randomness.
  • Casting decimal to double for range calculation: The decimal type has higher precision (28-29 significant digits) than double (15-16 digits). Casting to double for intermediate calculations loses precision. For financial applications, use integer-based approaches.
  • Sharing Random across threads without synchronization: System.Random is not thread-safe. Concurrent access can corrupt internal state, producing all zeros or throwing exceptions. Use Random.Shared, ThreadLocal<Random>, or locking.

Summary

  • Use random.NextDouble() for a random double in [0.0, 1.0), scale with min + NextDouble() * (max - min) for custom ranges
  • Use random.NextSingle() (.NET 6+) for random floats
  • Build NextDecimal() from random integers or scale from NextDouble() for the decimal type
  • Use RandomNumberGenerator for cryptographically secure random numbers
  • Use Random.Shared (.NET 6+) or ThreadLocal<Random> for thread-safe random generation

Course illustration
Course illustration