You can use decimal.Parse
to get the powers of ten out from a string made using Enumerable.Repeat
.
One special case which will need to be taken care of is when the absolute value of scale
exceeds 28
, which should work, e.g.
Scale(1e-28m, +56) == 1e+28
Scale(1e+28m, -56) == 1e-28
because calling decimal.Parse
will fail (the intermediate power cannot be represented in a decimal
value).
Code:
I've made a fiddle of the code below here.
/// <summary>
/// Scales value to move the decimal point by a certain number of places
/// </summary>
public static decimal Scale(decimal value, int places)
{
// Handle degenerate case
if ( value == 0 )
return 0;
// Handle the case when the power of ten will overflow.
// Split the problem up into two calls to Scale.
if ( Math.Abs(places) > 28 )
{
var intermediateNumberOfPlaces = places / 2;
var intermediateValue = Scale(value, intermediateNumberOfPlaces);
return Scale(intermediateValue, places - intermediateNumberOfPlaces);
}
// Normal cases
var powerOfTen = getPowerOfTen(Math.Abs(places));
if ( places > 0 )
return value * powerOfTen;
return value / powerOfTen;
}
private static ConcurrentDictionary<int, decimal> powersOfTen = new ConcurrentDictionary<int, decimal>();
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);
});
}
This method will handle cases like scale(1e-28m, 56)
or scale(1e28m, -56)
which (while probably won't happen much) should probably be accounted for.
Tests for Scale
:
Here are the validation tests I used to write the code:
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.01, 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");