Convert to fraction inches - c#

I need to convert a double value (centimeters) to a fraction value with this format: 3 1/64 (inches). After reading a lot about this and finding algorithms for converting into fractions, I think they are not good for what I need, because my fractions should be in these formats: ?/2, ?/4, ?/8, ?/16, ?/32, ?/64. I have seen conversion tables like this: table. And I think my best solution is to create a key, value list with all values in the table and for each number find the best approximation in the list.
For example: 3.21 cm. = 1.26378 in = 1 in + 0.26378. So, according the table linked, 0.26378 = 17/64. And the final result should be 1 17/64 inches.
So my questions are:
Is a good idea to have a list with the values in the table and find the closest value in order to give the fraction or it is better to create an algorithm for this?
In case it is fine to create a list with the values, how can I find the closest value of a given number in my list?

I suggest using simple math instead of table
private static string ToFraction64(double value) {
// denominator is fixed
int denominator = 64;
// integer part, can be signed: 1, 0, -3,...
int integer = (int) value;
// numerator: always unsigned (the sign belongs to the integer part)
// + 0.5 - rounding, nearest one: 37.9 / 64 -> 38 / 64; 38.01 / 64 -> 38 / 64
int numerator = (int) ((Math.Abs(value) - Math.Abs(integer)) * denominator + 0.5);
// some fractions, e.g. 24 / 64 can be simplified:
// both numerator and denominator can be divided by the same number
// since 64 = 2 ** 6 we can try 2 powers only
// 24/64 -> 12/32 -> 6/16 -> 3/8
// In general case (arbitrary denominator) use gcd (Greatest Common Divisor):
// double factor = gcd(denominator, numerator);
// denominator /= factor;
// numerator /= factor;
while ((numerator % 2 == 0) && (denominator % 2 == 0)) {
numerator /= 2;
denominator /= 2;
}
// The longest part is formatting out
// if we have an actual, not degenerated fraction (not, say, 4 0/1)
if (denominator > 1)
if (integer != 0) // all three: integer + numerator + denominator
return string.Format("{0} {1}/{2}", integer, numerator, denominator);
else if (value < 0) // negative numerator/denominator, e.g. -1/4
return string.Format("-{0}/{1}", numerator, denominator);
else // positive numerator/denominator, e.g. 3/8
return string.Format("{0}/{1}", numerator, denominator);
else
return integer.ToString(); // just an integer value, e.g. 0, -3, 12...
}
Tests
const double cmInInch = 2.54;
// 1 17/64
Console.Write(ToFraction64(3.21 / cmInInch));
// -1 17/64
Console.Write(ToFraction64(-1.26378));
// 3 1/4
Console.Write(ToFraction64(3.25001));
// 3 1/4
Console.Write(ToFraction64(3.24997));
// 5
Console.Write(ToFraction64(5.000001));
// -1/8
Console.Write(ToFraction64(-0.129));
// 1/8
Console.Write(ToFraction64(0.129));

Added display of feet
public static string ToFraction(this double source, int denominator)
{
var divider = denominator;
var inches = (int) Math.Abs(source);
var numerator = (int) ((Math.Abs(source) - Math.Abs(inches)) * divider + 0.5);
while (numerator % 2 == 0 && divider % 2 == 0)
{
numerator /= 2;
divider /= 2;
}
if (divider == numerator)
{
if (source < 0) inches--;
else inches++;
numerator = 0;
}
var feet = Math.DivRem(inches, 12, out inches);
var valueBuilder = new StringBuilder();
if (source + 1d / denominator < 0) valueBuilder.Insert(0, "-");
if (feet > 0)
{
valueBuilder.Append(feet);
valueBuilder.Append("'");
valueBuilder.Append("-");
}
valueBuilder.Append(inches);
if (numerator != 0)
{
valueBuilder.Append(" ");
valueBuilder.Append(numerator);
valueBuilder.Append("/");
valueBuilder.Append(divider);
}
valueBuilder.Append('"');
return valueBuilder.ToString();
}
All tests passed
[TestCase]
public void FractionTest()
{
Assert.AreEqual("0\"", 0d.ToFraction());
Assert.AreEqual("0\"", (-0d).ToFraction());
Assert.AreEqual("0\"", (-0.00001d).ToFraction());
Assert.AreEqual("1\"", 1d.ToFraction());
Assert.AreEqual("-1\"", (-1d).ToFraction());
Assert.AreEqual("0 1/8\"", 0.129.ToFraction());
Assert.AreEqual("-0 1/8\"", (-0.129).ToFraction());
Assert.AreEqual("-1 1/4\"", (-1.26378).ToFraction());
Assert.AreEqual("5\"", 5.000001.ToFraction());
Assert.AreEqual("3 1/4\"", 3.24997.ToFraction());
Assert.AreEqual("3 1/4\"", 3.25001.ToFraction());
Assert.AreEqual("1'-0\"", 12d.ToFraction());
Assert.AreEqual("1'-0 3/32\"", 12.1d.ToFraction());
Assert.AreEqual("1'-1\"", 13d.ToFraction());
Assert.AreEqual("1'-3 1/8\"", 15.125d.ToFraction());
Assert.AreEqual("1'-0\"", 12.00001d.ToFraction());
Assert.AreEqual("-1'-0\"", (-12.00001d).ToFraction());
Assert.AreEqual("-2'-1 7/32\"", (-25.231d).ToFraction());
}

taking the code of Dmitry Bychenko, we need to add the case "else if (denominator == numerator)" if fraction equals to 1 to add 1 if the value is positive or remove 1 if the value is negative (ex denominator/numerator = 64/64)
private static string ToFraction64(double value)
{
// denominator is fixed
int denominator = 64;
// integer part, can be signed: 1, 0, -3,...
int integer = (int)value;
// numerator: always unsigned (the sign belongs to the integer part)
// + 0.5 - rounding, nearest one: 37.9 / 64 -> 38 / 64; 38.01 / 64 -> 38 / 64
int numerator = (int)((Math.Abs(value) - Math.Abs(integer)) * denominator + 0.5);
// some fractions, e.g. 24 / 64 can be simplified:
// both numerator and denominator can be divided by the same number
// since 64 = 2 ** 6 we can try 2 powers only
// 24/64 -> 12/32 -> 6/16 -> 3/8
// In general case (arbitrary denominator) use gcd (Greatest Common Divisor):
// double factor = gcd(denominator, numerator);
// denominator /= factor;
// numerator /= factor;
while ((numerator % 2 == 0) && (denominator % 2 == 0))
{
numerator /= 2;
denominator /= 2;
}
// The longest part is formatting out
// if we have an actual, not degenerated fraction (not, say, 4 0/1)
if (denominator > 1)
if (integer != 0) // all three: integer + numerator + denominator
return string.Format("{0} {1}/{2}", integer, numerator, denominator);
else if (value < 0) // negative numerator/denominator, e.g. -1/4
return string.Format("-{0}/{1}", numerator, denominator);
else // positive numerator/denominator, e.g. 3/8
return string.Format("{0}/{1}", numerator, denominator);
//if fraction equals to 1 we add 1 if the value is positive or remove 1 if the value is negative (ex denominator/numerator = 64/64)
else if (denominator == numerator)
{
if (value < 0) // negative numerator/denominator, e.g. -1/4
integer--;
else // positive numerator/denominator, e.g. 3/8
integer++;
return integer.ToString();
}
else
return integer.ToString(); // just an integer value, e.g. 0, -3, 12...
}

My function is easier and more simple..
It return an array of three integer {Inches, Numerator, Denominator} from a decimal value, the fracBase argument mean the precision as 16, 32, 64, 128 ....
public static int[] GetImpFractions(decimal value, int fracBase = 32)
{
int[] result = { 0, 0, 0 };
result[0] = (int)Math.Truncate(value);
decimal num = (value - (decimal)result[0]);
num *= fracBase;
decimal denom = fracBase;
if (num > 0)
{
while (num % 2 == 0)
{
num /= 2;
denom /= 2;
}
if (num == 1 && denom == 1)
{
result[0] += 1;
num = 0;
denom = 0;
}
result[1] = (int)Math.Truncate(num);
result[2] = (int)Math.Truncate(denom);
}
return result;
}

Related

C# Decimal Precision Improvement in Powers and Fibonacci

I am trying to solve the Fibonacci sequence with both negative numbers and large numbers and came up with the following code and algorithm. I am certain the algorithm works, but the issue I am having is for very large numbers the precision of the result is incorrect. Here is the code:
public class Fibonacci
{
public static BigInteger fib(int n)
{
decimal p = (decimal) (1 + Math.Sqrt(5)) / 2;
decimal q = (decimal) (1 - Math.Sqrt(5)) / 2;
decimal r = (decimal) Math.Sqrt(5);
Console.WriteLine("n: {0} p: {1}, q: {2}, t: {3}",
n,
p,
q,
(Pow(p, n) - Pow(q, n)) / r);
return (BigInteger) (Decimal.Round((Pow(p, n) - Pow(q, n)) / r));
}
public static decimal Pow(decimal x, int y)
{
if(y < 0)
return 1 / Pow(x, -1 * y);
else if(y == 0)
return 1;
else if(y % 2 == 0)
{
decimal z = Pow(x, y / 2);
return z * z;
}
else if(y % 2 == 1)
return Pow(x, y - 1) * x;
else
return 1;
}
Small values of If we take a large number like -96 to get the Fibonacci for, I get a result of -51680708573203484173 but the real number is -51680708854858323072. I checked the rounding was OK, but it appears somewhere along the way my result is losing precision and not saving its values correctly. I thought using decimals would solve this precision issue (previously used doubles), but that did not work.
Where in my code am I incorrectly missing precision or is there another issue with my code I am misdiagnosing?
Try this.
public static BigInteger Fibonacci(int n)
{
BigInteger a = 0;
BigInteger b = 1;
for (int i = 31; i >= 0; i--)
{
BigInteger d = a * (b * 2 - a);
BigInteger e = a * a + b * b;
a = d;
b = e;
if ((((uint)n >> i) & 1) != 0)
{
BigInteger c = a + b;
a = b;
b = c;
}
}
return a;
}
Good Luck!
As you wrote, decimal has approximately 28 decimal digits of precision. However, Math.Sqrt(5), being a double, does not.
Using a more accurate square root of 5 enables this algorithm to stay exact for longer, though of course it is still limited by precision eventually, just later.
public static BigInteger fib(int n)
{
decimal sqrt5 = 2.236067977499789696409173668731276235440618359611525724270m;
decimal p = (1 + sqrt5) / 2;
decimal q = (1 - sqrt5) / 2;
decimal r = sqrt5;
return (BigInteger) (Decimal.Round((Pow(p, n) - Pow(q, n)) / r));
}
This way fib(96) = 51680708854858323072 which is correct. However, it becomes wrong again at 128.

Converting fractions to binary string?

To convert an integer value, manually, to binary string you (one technique is to) continuously divide by 2 till the quotient > 0 and append the remainder in reverse order.
string decimalToBinary(int n)
{
string binary = string.Empty;
while (n > 0)
{
// get the LSB
int remainder = n % 2;
// truncate the LSB
n /= 2;
// insert remainder in front
binary = remainder.ToString() + binary;
}
return binary;
}
However, I can't figure out how to convert a fraction (floating point number like for example -0.30), to binary string. More specifically what algorithm should I use. Could anyone suggest an idea?
To convert an integer value, manually
Note that since your input is a an integer which can be negative or zero. But your condition for the while loop is while (n > 0)
Therefore, your code right now cannot handle the situation when the input is 0 (it will return string.Empty) or negative (it will return nothing).
To fix it, you may consider to change your input to uint and make special case for n == 0:
string decimalToBinary(uint n) //note this uint
{
if (n == 0) //special case
return "0";
string binary = string.Empty;
while (n > 0)
{
// get the LSB
uint remainder = n % 2;
// truncate the LSB
n /= 2;
// insert remainder in front
binary = remainder.ToString() + binary;
}
return binary;
}
Or you change it internally:
private string decimalToBinary(int n) {
string binary = string.Empty;
if (n == 0)
return "0";
uint p = (uint)n; //note this cast
while (p > 0) {
// get the LSB
uint remainder = p % 2;
// truncate the LSB
p /= 2;
// insert remainder in front
binary = remainder.ToString() + binary;
}
return binary;
}
Then you should get what you want.
Here is a C++ implementation for fraction to binary conversion:
double frac = .1;
int digits = 1, intpart;
cout <<".";
while (digits < 32 && frac != 0.)
{
frac = frac * 2;
intpart = frac;
frac = frac - intpart;
cout << intpart;
digits++;
}
Output: .0001100110011001100110011001100
or using recursion:
#include <iostream>
using namespace std;
void fractobin(double frac, int digits);
//==========================================================
int main()
{
cout <<".";
fractobin(.1, 1);
return 0;
}
//==========================================================
void fractobin(double frac, int digits)
{
int intpart;
if (digits >=32 || frac==0.)
{
return;
}
else
{
frac = frac * 2;
intpart = frac;
cout << intpart;
fractobin(frac - intpart, ++digits);
return;
}
}

Why this sin(x) function in C# return NaN instead of a number

I have this function wrote in C# to calc the sin(x). But when I try with x = 3.14, the printed result of sin X is NaN (not a number),
but when debugging, its is very near to 0.001592653
The value is not too big, neither too small. So how could the NaN appear here?
static double pow(double x, int mu)
{
if (mu == 0)
return 1;
if (mu == 1)
return x;
return x * pow(x, mu - 1);
}
static double fact(int n)
{
if (n == 1 || n == 0)
return 1;
return n * fact(n - 1);
}
static double sin(double x)
{
var s = x;
for (int i = 1; i < 1000; i++)
{
s += pow(-1, i) * pow(x, 2 * i + 1) / fact(2 * i + 1);
}
return s;
}
public static void Main(String[] param)
{
try
{
while (true)
{
Console.WriteLine("Enter x value: ");
double x = double.Parse(Console.ReadLine());
var sinX = sin(x);
Console.WriteLine("Sin of {0} is {1}: " , x , sinX);
Console.ReadLine();
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
It fails because both pow(x, 2 * i + 1) and fact(2 * i + 1) eventually return Infinity.
In my case, it's when x = 4, i = 256.
Note that pow(x, 2 * i + 1) = 4 ^ (2 * 257) = 2.8763090157797054523668883052624395737887631663 × 10^309 - a stupidly large number which is just over the max value of a double, which is approximately 1.79769313486232 x 10 ^ 308.
You might be interested in just using Math.Sin(x)
Also note that fact(2 * i + 1) = 513! =an even more ridiculously large number which is more than 10^1000 times larger than the estimated number of atoms in the observable universe.
When x == 3.14 and i == 314 then you get Infinity:
?pow(-1, 314)
1.0
?pow(x, 2 * 314 + 1)
Infinity
? fact(2 * 314 + 1)
Infinity
The problem here is an understanding of floating point representation of 'real' numbers.
Double numbers while allowing a large range of values only has a precision of 15 to 17 decimal digits.
In this example we are calculating a value between -1 and 1.
We calculate the value of the sin function by using the series expansion of it which is basically a the sum of terms. In that expansion the terms become smaller and smaller as we go along.
When the terms have reached a value less than 1e-17 adding them to what is already there will not make any difference. This is so because we only have 52 bit of precision which are used up by the time we get to a term of less than 1e-17.
So instead of doing a constant 1000 loops you should do something like this:
static double sin(double x)
{
var s = x;
for (int i = 1; i < 1000; i++)
{
var term = pow(x, 2 * i + 1) / fact(2 * i + 1);
if (term < 1e-17)
break;
s += pow(-1, i) * term;
}
return s;
}

C# Check if a decimal has more than 3 decimal places?

I have a situation that I cannot change: one database table (table A) accepts 6 decimal places, while a related column in a different table (table B) only has 3 decimal places.
I need to copy from A to B, but if A has more than 3 decimal places the extra data will be lost. I cant change the table definition but I can add a workaround. So I'm trying to find out how to check if a decimal has more than 3 decimal places or not?
eg
Table A
Id, Qty, Unit(=6dp)
1, 1, 0.00025
2, 4000, 0.00025
Table B
Id, TotalQty(=3dp)
I want to be able to find out if Qty * Unit from Table A has more than 3 decimals (row 1 would fail, row 2 would pass):
if (CountDecimalPlaces(tableA.Qty * tableA.Unit) > 3)
{
return false;
}
tableB.TotalQty = tableA.Qty * tableA.Unit;
How would I implement the CountDecimalPlaces(decimal value) {} function?
You could compare the value of the number rounded to 3 decimal places with the original value.
if (Decimal.Round(valueDecimal, 3) != valueDecimal)
{
//Too many decimals
}
This works for 3 decimal places, and it can be adapted for a generic solution:
static bool LessThan3DecimalPlaces(decimal dec)
{
decimal value = dec * 1000;
return value == Math.Floor(value);
}
static void Test()
{
Console.WriteLine(LessThan3DecimalPlaces(1m * 0.00025m));
Console.WriteLine(LessThan3DecimalPlaces(4000m * 0.00025m));
}
For a real generic solution, you'll need to "deconstruct" the decimal value in its parts - take a look at Decimal.GetBits for more information.
Update: this is a simple implementation of a generic solution which works for all decimals whose integer part is less than long.MaxValue (you'd need something like a "big integer" for a trully generic function).
static decimal CountDecimalPlaces(decimal dec)
{
Console.Write("{0}: ", dec);
int[] bits = Decimal.GetBits(dec);
ulong lowInt = (uint)bits[0];
ulong midInt = (uint)bits[1];
int exponent = (bits[3] & 0x00FF0000) >> 16;
int result = exponent;
ulong lowDecimal = lowInt | (midInt << 32);
while (result > 0 && (lowDecimal % 10) == 0)
{
result--;
lowDecimal /= 10;
}
return result;
}
static void Foo()
{
Console.WriteLine(CountDecimalPlaces(1.6m));
Console.WriteLine(CountDecimalPlaces(1.600m));
Console.WriteLine(CountDecimalPlaces(decimal.MaxValue));
Console.WriteLine(CountDecimalPlaces(1m * 0.00025m));
Console.WriteLine(CountDecimalPlaces(4000m * 0.00025m));
}
This is a very simple one line code to get count of decimals in a Decimal:
decimal myDecimal = 1.000000021300010000001m;
byte decimals = (byte)((Decimal.GetBits(myDecimal)[3] >> 16) & 0x7F);
Multiplying a number with 3 decimal places by 10 to the power of 3 will give you a number with no decimal places. It's a whole number when the modulus % 1 == 0. So I came up with this...
bool hasMoreThanNDecimals(decimal d, int n)
{
return !(d * (decimal)Math.Pow(10, n) % 1 == 0);
}
Returns true when n is less than (not equal) to the number of decimal places.
The basics is to know how to test if there are decimal places, this is done by comparing the value to its rounding
double number;
bool hasDecimals = number == (int) number;
Then, to count 3 decimal places, you just need to do the same for your number multiplied by 1000:
bool hasMoreThan3decimals = number*1000 != (int) (number * 1000)
All of the solutions proposed so far are not extensible ... fine if you are never going to check a value other than 3, but I prefer this because if the requirement changes the code to handle it is already written. Also this solution wont overflow.
int GetDecimalCount(decimal val)
{
if(val == val*10)
{
return int.MaxValue; // no decimal.Epsilon I don't use this type enough to know why... this will work
}
int decimalCount = 0;
while(val != Math.Floor(val))
{
val = (val - Math.Floor(val)) * 10;
decimalCount++;
}
return decimalCount;
}
carlosfigueira solution will need to check for 0 otherwise "while ((lowDecimal % 10) == 0)" will produce an infinity loop when called with dec = 0
static decimal CountDecimalPlaces(decimal dec)
{
if (dec == 0)
return 0;
int[] bits = Decimal.GetBits(dec);
int exponent = bits[3] >> 16;
int result = exponent;
long lowDecimal = bits[0] | (bits[1] >> 8);
while ((lowDecimal % 10) == 0)
{
result--;
lowDecimal /= 10;
}
return result;
}
Assert.AreEqual(0, DecimalHelper.CountDecimalPlaces(0m));
Assert.AreEqual(1, DecimalHelper.CountDecimalPlaces(0.5m));
Assert.AreEqual(2, DecimalHelper.CountDecimalPlaces(10.51m));
Assert.AreEqual(13, DecimalHelper.CountDecimalPlaces(10.5123456978563m));
One more option based on #RodH257's solution, but reworked as an extension method:
public static bool HasThisManyDecimalPlacesOrLess(this decimal value, int noDecimalPlaces)
{
return (Decimal.Round(value, noDecimalPlaces) == value);
}
You can then call that as:
If !(tableA.Qty * tableA.Unit).HasThisManyDecimalPlacesOrLess(3)) return;
There is probably a more elegant way to do this, but off the top of my head I would try
a = multiply by 1000
b = truncate a
if (b != a) then there is additional precision that has been lost
bool CountDecimalPlaces(decimal input)
{
return input*1000.0 == (int) (input*1000);
}
Here is my version:
public static int CountDecimalPlaces(decimal dec)
{
var a = Math.Abs(dec);
var x = a;
var count = 1;
while (x % 1 != 0)
{
x = a * new decimal(Math.Pow(10, count++));
}
var result = count - 1;
return result;
}
I tried first #carlosfigueira/#Henrik Stenbæk, but their version does not work with 324000.00m
TEST:
Console.WriteLine(CountDecimalPlaces(0m)); //0
Console.WriteLine(CountDecimalPlaces(0.5m)); //1
Console.WriteLine(CountDecimalPlaces(10.51m)); //2
Console.WriteLine(CountDecimalPlaces(10.5123456978563m)); //13
Console.WriteLine(CountDecimalPlaces(324000.0001m)); //4
Console.WriteLine(CountDecimalPlaces(324000.0000m)); //0
could you convert it to a string and just do a len function or would that not cover your situation?
follow up question:
would 300.4 be ok?
Public Function getDecimalCount(decWork As Decimal) As Integer
Dim intDecimalCount As Int32 = 0
Dim strDecAbs As String = decWork.ToString.Trim("0")
intDecimalCount = strDecAbs.Substring(strDecAbs.IndexOf(".")).Length -1
Return intDecimalCount
End Function

Excel RoundUp vs .NET Math.Round

in Excel, =ROUNDUP(474.872126666666, 2) -> 474.88
in .NET,
Math.Round(474.87212666666666666666666666667, 2, MidpointRounding.ToEven) // 474.87
Math.Round(474.87212666666666666666666666667, 2, MidpointRounding.AwayFromZero) // 474.87
My client want Excel rounding result, is there any way I can get 474.88 in .NET?
Thanks a lot
double ROUNDUP( double number, int digits )
{
return Math.Ceiling(number * Math.Pow(10, digits)) / Math.Pow(10, digits);
}
Math.Ceiling is what you're looking for.
Here's my try on a solution that behaves like Excel ROUNDUP function.
I tried to cover cases such as: negative decimal numbers, negative digits (yep Excel supports that), large decimal values
public static decimal RoundUp(decimal number, int digits)
{
if (digits > 0)
{
// numbers will have a format like +/-1.23, where the fractional part is optional if numbers are integral
// Excel RoundUp rounds negative numbers as if they were positive.
// To simulate this behavior we will use the absolute value of the number
// E.g. |1.23| = |-1.23| = 1.23
var absNumber = Math.Abs(number);
// Now take the integral part (E.g. for 1.23 is 1)
var absNumberIntegralPart = Decimal.Floor(absNumber);
// Now take the fractional part (E.g. for 1.23 is 0.23)
var fraction = (absNumber - absNumberIntegralPart);
// Multiply fractional part by the 10 ^ number of digits we're rounding to
// E.g. For 1.23 with rounded to 1 digit it will be 0.23 * 10^1 = 2.3
// Then we round that value UP using Decimal.Ceiling and we transform it back to a fractional part by dividing it by 10^number of digits
// E.g. Decimal.Ceiling(0.23 * 10) / 10 = Decimal.Ceiling(2.3) / 10 = 3 / 10 = 0.3
var tenPower = (decimal)Math.Pow(10, digits);
var fractionRoundedUp = Decimal.Ceiling(fraction * tenPower) / tenPower;
// Now we add up the absolute part with the rounded up fractional part and we put back the negative sign if needed
// E.g. 1 + 0.3 = 1.3
return Math.Sign(number) * (absNumberIntegralPart + fractionRoundedUp);
} else if (digits == 0)
{
return Math.Sign(number) * Decimal.Ceiling(Math.Abs(number));
} else if (digits < 0)
{
// negative digit rounding means that for RoundUp(149.12, -2) we will discard the fractional part, shift the decimal point on the left part 2 places before rounding up
// then replace all digits on the right of the decimal point with zeroes
// E.g. RoundUp(149.12, -2). Shift decimal point 2 places => 1.49. Now roundup(1.49) = 2 and we put 00 instead of 49 => 200
var absNumber = Math.Abs(number);
var absNumberIntegralPart = Decimal.Floor(absNumber);
var tenPower = (decimal)Math.Pow(10, -digits);
var absNumberIntegraPartRoundedUp = Decimal.Ceiling(absNumberIntegralPart / tenPower) * tenPower;
return Math.Sign(number)*absNumberIntegraPartRoundedUp;
}
return number;
}
[TestMethod]
public void Can_RoundUp_Correctly()
{
Assert.AreEqual(1.466m, MathExtensions.RoundUp(1.4655m, 3));
Assert.AreEqual(-1.466m, MathExtensions.RoundUp(-1.4655m, 3));
Assert.AreEqual(150m, MathExtensions.RoundUp(149.001m, 0));
Assert.AreEqual(-150m, MathExtensions.RoundUp(-149.001m, 0));
Assert.AreEqual(149.2m, MathExtensions.RoundUp(149.12m, 1));
Assert.AreEqual(149.12m, MathExtensions.RoundUp(149.12m, 2));
Assert.AreEqual(1232m, MathExtensions.RoundUp(1232, 2));
Assert.AreEqual(200m, MathExtensions.RoundUp(149.123m, -2));
Assert.AreEqual(-200m, MathExtensions.RoundUp(-149.123m, -2));
Assert.AreEqual(-20m, MathExtensions.RoundUp(-12.4655m, -1));
Assert.AreEqual(1.67m, MathExtensions.RoundUp(1.666666666666666666666666666m, 2));
Assert.AreEqual(1000000000000000000000000000m, MathExtensions.RoundUp(999999999999999999999999999m, -2));
Assert.AreEqual(10000000000000m, MathExtensions.RoundUp(9999999999999.999m, 2));
}
Here is the correct calculation for ROUNDUP and ROUNDDOWN:
private static object RoundDown(List<Expression> p)
{
var target = (decimal)p[0].Evaluate();
var digits = (decimal)p[1].Evaluate();
if (target < 0) return (Math.Ceiling((double)target * Math.Pow(10, (int)digits)) / Math.Pow(10, (int)digits));
return Math.Floor((double)target * Math.Pow(10, (int)digits)) / Math.Pow(10, (int)digits);
}
private static object RoundUp(List<Expression> p)
{
var target = (decimal)p[0].Evaluate();
var digits = (decimal)p[1].Evaluate();
if (target < 0) return (Math.Floor((double)target * Math.Pow(10, (int)digits)) / Math.Pow(10, (int)digits));
return Math.Ceiling((double)target * Math.Pow(10, (int)digits)) / Math.Pow(10, (int)digits);
}

Categories