using System.Collections.Concurrent;
using System.Globalization;
public static void Main()
Assert.AreEqual(1, Scale(1, 0), "Invariant scale failed");
Assert.AreEqual(0, Scale(0, 100), "Scale of 0 failed");
Assert.AreEqual(100, Scale(1, 2), "Scale(1, 2) failed");
Assert.AreEqual(0.01m, Scale(1, -2), "Scale(1, -2) failed");
Assert.AreEqual(1, Scale(0.01m, 2), "Scale(0.01, 2) failed");
Assert.AreEqual(1, Scale(100, -2), "Scale(100, -2) failed");
var large = Scale(1, 28);
var small = Scale(1, -28);
var shouldBeLarge = Scale(small, 56);
var shouldBeSmall = Scale(large, -56);
Assert.AreEqual(large, shouldBeLarge, "scaling 1e-28 by 56 failed");
Assert.AreEqual(small, shouldBeSmall, "scaling 1e28 by -56 failed");
Console.WriteLine("All tests passed");
private static ConcurrentDictionary<int, decimal> powersOfTen = new ConcurrentDictionary<int, decimal>();
public static decimal Scale(decimal value, int places)
if ( Math.Abs(places) > 28 )
var intermediateNumberOfPlaces = places / 2;
var intermediateValue = Scale(value, intermediateNumberOfPlaces);
return Scale(intermediateValue, places - intermediateNumberOfPlaces);
var powerOfTen = getPowerOfTen(Math.Abs(places));
return value * powerOfTen;
return value / powerOfTen;
private static decimal getPowerOfTen(int power)
return powersOfTen.GetOrAdd(power, p =>
var powerAsString = "1" + string.Concat(Enumerable.Repeat("0", p));
return decimal.Parse(powerAsString, CultureInfo.InvariantCulture);
public static class Assert
public static void AreEqual(decimal expected, decimal actual, string message)
throw new Exception(message);