I have a problem that I think may not have an answer other then completely rethinking the application, but hopefully you guys can prove me wrong!
I have an application that uses an Exchange Rate to back calculate a Base value from a Currency value to four decimal places. The calculation is pretty simple, and passes all the relevant unit tests giving correct results each time:
public static decimal GetBaseValue(decimal currencyAmount, RateOperator rateOperator,
double exchangeRate)
{
if (exchangeRate <= 0)
{
throw new ArgumentException(ErrorType.
ExchangeRateLessThanOrEqualToZero.ErrorInfo().Description);
}
decimal baseValue = 0;
if (currencyAmount != 0)
{
switch (rateOperator)
{
case RateOperator.Divide:
baseValue = Math.Round(currencyAmount * Convert.ToDecimal(exchangeRate),
4, MidpointRounding.AwayFromZero);
break;
case RateOperator.Multiply:
baseValue = Math.Round(currencyAmount / Convert.ToDecimal(exchangeRate),
4, MidpointRounding.AwayFromZero);
break;
default:
throw new ArgumentOutOfRangeException(nameof(rateOperator));
}
}
return baseValue;
}
I also have the equivalent to calculate currency from base, not shown to avoid confusing the issue and in any case pretty much identical code apart from parameter names and the reversal of the math operators in the switch statement.
My problem comes when I need to apply this process to a transaction which has multiple lines. The rule is that the total of the debits must equal the total of the credits, but some numbers are out by a fraction of a penny. This is because we are totalling the result of two or more individually converted numbers against a single conversion, and I believe that the accumulated rounding causes the error.
Let us assume the following:
Item value of $1.00
VAT value of $0.20 (20%)
Exchange rate of 1.4540 dollars to the pound
Now let us review an example transaction:
Line # Debit Credit
1 $1.20
2 $1.00
3 $0.20
This passes the test, as total credits match total debits.
When we convert (divide each dollar value by 1.454), we see the problem:
Line # Debit Credit
1 £0.8253
2 £0.6878
3 £0.1376
========================
Total £0.8254 £0.8253
========================
This fails and breaks the rule, I believe as a result of the two sets of rounding in the debit column and only one set on the credit column. Yet, I need to be able to calculate backwards and forwards with accuracy, and I need to make sure that the transaction balances in Currency and in Base across all the lines.
So to my question: how best to address this problem? Anyone experienced something similar, and if so do you mind sharing how you resolved it?
EDIT - The Solution I Used
Following the answer from Charles that pointed me absolutely in the right direction I am posting my working method for the benefit of anyone else who might face a similar issue. Whilst understanding that this specific code may not be suitable for a direct copy and paste solution, I hope that the comments and the procedure followed will be of help:
private static void SetBaseValues(Transaction transaction)
{
// Get the initial currency totals
decimal currencyDebitRunningTotal = transaction.CurrencyDebitTotal;
decimal currencyCreditRunningTotal = transaction.CurrencyCreditTotal;
// Only one conversion, but we do one per column
// Note that the values should be the same anyway
// or the transaction would be invalid
decimal baseDebitRunningTotal =
Functions.GetBaseValue(currencyDebitRunningTotal,
transaction.MasterLine.RateOperator,
transaction.MasterLine.ExchangeRate);
decimal baseCreditRunningTotal =
Functions.GetBaseValue(currencyCreditRunningTotal,
transaction.MasterLine.RateOperator,
transaction.MasterLine.ExchangeRate);
// Create a list of transaction lines that belong to this transaction
List<TransactionLineBase> list = new List<TransactionLineBase>
{ transaction.MasterLine };
list.AddRange(transaction.TransactionLines);
// If there is no tax line, don't add a null entry
// as that would cause conversion failure
if (transaction.TaxLine != null)
{
list.Add(transaction.TaxLine);
}
// Sort the list ascending by value
var workingList = list.OrderBy(
x => x.CurrencyCreditAmount ?? 0 + x.CurrencyDebitAmount ?? 0).ToList();
// Iterate the lines excluding any entries where Credit and Debit
// values are both null (this is possible on some rows on
// some transactions types e.g. Reconciliations
foreach (var line in workingList.Where(
line => line.CurrencyCreditAmount != null ||
line.CurrencyDebitAmount != null))
{
if (transaction.CanConvertCurrency)
{
SetBaseValues(line);
}
else
{
var isDebitLine = line.CurrencyCreditAmount == null;
if (isDebitLine)
{
if (line.CurrencyDebitAmount != 0)
{
line.BaseDebitAmount =
line.CurrencyDebitAmount ?? 0 /
currencyDebitRunningTotal * baseDebitRunningTotal;
currencyDebitRunningTotal -=
line.CurrencyDebitAmount ?? 0;
baseDebitRunningTotal -= line.BaseDebitAmount ?? 0;
}
}
else
{
if (line.CurrencyCreditAmount != 0)
{
line.BaseCreditAmount =
line.CurrencyCreditAmount ?? 0/
currencyCreditRunningTotal*baseCreditRunningTotal;
currencyCreditRunningTotal -= line.CurrencyCreditAmount ?? 0;
baseCreditRunningTotal -= line.BaseCreditAmount ?? 0;
}
}
}
}
}
This does rather depend on the situation, but one option for this is to only convert the totals and then allocate this proportionately to each of the parts using a reducing balance method. This ensures that the sum of the parts will always equal the total exactly.
Your debit and credit columns both add up to $1.20, so you convert this at your rate giving you £0.8253.
You then proportionately allocate this to your debit amounts from the smallest up to the largest. The theory behind the sorting is that you're less likely to care about the extra/missing pennies of a larger number.
So you start with the totals of $1.20 and £0.6878 and then calculate the proportion of your converted balance that applies to your smallest dollar amount:
$0.20 / $1.20 * 0.8253 = £0.1376
You then deduct the amounts from your totals (this is the 'reducing balance' part):
$1.20 - $0.20 = $1.00
£0.8253 - £0.1376 = £0.6877
And then calculate the next largest (as you only have 1 more amount in this example, this is trivial):
$1.00 / $1.00 * £0.6877 = £0.6877
So this gives you:
Line # Debit Credit
1 £0.8253
2 £0.6877
3 £0.1376
========================
Total £0.8253 £0.8253
========================
This is typically a business problem not a programming one. The results you're seeing are a result of mathematical rounding. In financial calculations the business will decide when the exchange rate is applied and it has to be applied once. For example, in your case it might be decided to apply it to the item value and then multiply by the VAT to get your total. This is also typically present in your regions accepted accounting/financial accepted standards.
Related
I have task, that I'm unsure on how i should approach.
there's a list of doubles, and i need to group them together to add up to a specific value.
Say i have:
14.6666666666666,
14.6666666666666,
2.37499999999999,
1.04166666666665,
1.20833333333334,
1.20833333333334,
13.9583333333333,
1.20833333333334,
3.41666666666714,
3.41666666666714,
1.20833333333334,
1.20833333333334,
14.5416666666666,
1.20833333333335,
1.04166666666666,
And i would like to group into set values such as 12,14,16
I would like to take the highest value in the list then group it with short ones to equal the closest value above.
example:
take double 14.6666666666666, and group it with 1.20833333333334 to bring me close to 16, and if there are anymore small doubles left in the list, group them with that as well.
Then move on to the next double in the list..
That's literally the "Cutting stock Problem" (Sometimes called the 1 Dimensional Bin Packing Problem). There are a number of well documented solutions.
The only way to get the "Optimal" solution (other than a quantum computer) is to cycle through every combination, and select the best outcome.
A quicker way to get an "OK" solution is called the "First Fit Algorithm". It takes the requested values in the order they come, and removes them from the first piece of material that can fulfill the request.
The "First Fit Algorithm" can be slightly improved by pre-ordering the the values from largest to smallest, and pre-ordering the materials from smallest to largest. You could also uses the material that is closest to being completely consumed by the request, instead of the first piece that can fulfill the request.
A compromise, but one that requires more code is a "Genetic Algorithm". This is an over simplification, but you could use the basic idea of the "First Fit Algorithm", but randomly swap two of the values before each pass. If the efficiency increases, you keep the change, and if it decreases, you go back to the last state. Repeat until a fixed amount of time has passed or until you're happy.
Put the doubles in a list and sort them. Grab the highest value that is less than the target to start. Then loop through from the start of the list adding the values until you reach a point where adding the value will put you over the limit.
var threshold = 16;
List<double> values = new List<double>();
values.Add(14.932034);
etc...
Sort the list:
values = values.OrderBy(p => p).ToList();
Grab the highest value that is less than your threshold:
// Highest value under threshold
var highestValue = values.Where(x => x < threshold).Max();
Now perform your search and calculations until you reach your solution:
currentValue = highestValue
Console.WriteLine("Starting with: " + currentValue);
foreach(var val in values)
{
if(currentValue + val <= theshold)
{
currentValue = currentValue + val;
Console.WriteLine(" + " + val.ToString());
}
else
break;
}
Console.WriteLine("Finished with: " + currentValue.ToString());
Console.ReadLine();
Repeat the process for the next value and so on until you've output all of the solutions you want.
I have a dataset of voltages (Sampled every 500ms). Lets say it looks something like this (In an array):
0ms -> 1.4v
500ms -> 1.3v
1000ms -> 1.2v
1500ms -> 1.5v
2000ms -> 1.3v
2500ms -> 1.3v
3000ms -> 1.2v
3500ms -> 1.3v
Assuming the transition between readings is linear (IE: 250ms = 1.35v), how would I go about calculating the total % of time that the reading is above or equal to 1.3v?
I was initially going to just get % of values that are >= 1.3v (IE: 6/8 in sample array), however this only works if the angle between points is 45 degrees. I am assuming I have to do something like create a line from point 1 to point 2 and find the intercept with the base line (1.3v). Then do the same for point 2 and point 3 and find the distance between both intersects (Say 700ms) then repeat for all points and get as a % of total sample time.
EDIT
Maybe I wasn't clear when I initially asked. I need help with identifying how I can perform these calculations, IE: objects/classes that I can use to help me virtually graph these lines and perform these calculations or any 3rd party math packages that might offer these capabilities.
The important part is not to think in data points, but in intervals. Every interval (e.g. 0-500, 500-1000, ...) is one of three cases (starting with float variables above and below both 0):
Trivial: Both start and end point are below your threshold - below += 1
Trivial: Both start and end point are above your threshold - above += 1
Interesting: One point is below, one above your threshold. Let's call the smaller value min and the higher value max. Now we do above += (max-threshold)/(max-min) and below += (threshold-min)/(max-min), so we linearily distribute this interval between both states.
Finally normalize the results by dividing both above and below by the number of intervals. This will give you a pair of numbers, that represent the fractions, i.e. that add up to 1 modulo rounding errors. Ofcourse multiplication with 100 gives you the percentages.
EDIT
#phoog pointed out in the comment, that I did not mention an "equal" case. This is by design, as your question already contains that: You chose >= as a comparison, so I definitly ment to use the same comparison here.
If I've understood the problem correctly, you can use a class like this to hold each entry:
public class DataEntry
{
public DataEntry(int time, double reading)
{
Time = time;
Reading = reading;
}
public int Time { get; set; }
public double Reading { get; set; }
}
And then the following link statement to get segments above 1.3:
var entries = new List<DataEntry>()
{
new DataEntry(0, 1.4),
new DataEntry(500, 1.3),
new DataEntry(1000, 1.2),
new DataEntry(1500, 1.5),
new DataEntry(2000, 1.3),
new DataEntry(2500, 1.3),
new DataEntry(3000, 1.2),
new DataEntry(3500, 1.3)
};
double totalTime = entries
.OrderBy(e => e.Time)
.Take(entries.Count - 1)
.Where((t, i) => t.Reading >= 1.3 && entries[i + 1].Reading >= 1.3)
.Sum(t => 500);
var perct = (totalTime / entries.Max(e => e.Time));
This should give you the 500ms segments that remained above 1.3.
I am trying to build a help function in my guess the number game, whereby the user gets the first digit of the number he/she has to guess. So if the generated number is 550, he will get the 5.
I have tried a lot of things, maybe one of you has an idea what is wrong?
public partial class Class3
{
public Class3()
{
double test = Convert.ToDouble(globalVariableNumber.number);
while (test > 10)
{
double firstDigit = test / 10;
test = Math.Round(test);
globalVariableNumber.helpMe = Convert.ToString(firstDigit);
}
}
}
Under the helpButton clicked I have:
private void helpButton_Click(object sender, EventArgs e)
{
label3.Text = globalVariableNumber.helpMe;
label3.AutoSize = true;
That is my latest try, I putted all of this in a custom class. In the main I putted the code to show what is in the helpMe string.
If you need more code please tell me
Why not ToString the number and use Substring to get the first character?
var number = 550;
var result = number.ToString().Substring(0, 1);
If for some reason you dont want to use string manipulation you could do this mathematically like this
var number = 550;
var result = Math.Floor(number / Math.Pow(10, Math.Floor(Math.Log10(number))));
What's wrong - you have an infinite while loop there. Math.Round(test) will leave the value of test unchanged after the first iteration.
You may have intended to use firstDigit as the variable controlling the loop.
Anyway, as suggested by others, you can set helpMe to the first digit by converting to a string and using the first character.
As an aside, you should consider supplying the number as a parameter and returning the helpMe string from the method. Your current approach is a little brittle.
The problem with your code is that you are doing the division and storing that in a separate variable, then you round the original value. That means that the original value only changes in the first iteration of the loop (and is only rounded, not divided), and unless that happens to make the loop condition false (i.e. for values between 10 and 10.5), the loop will never end.
Changes:
Use an int intead of a double, that gets you away from a whole bunch of potential precision problems.
Use the >= operator rather than >. If you get the value 10 then you want the loop to go on for another iteration to get a single digit.
You would use Math.Floor instead of Math.Round as you don't want the first digit to be rounded up, i.e. getting the first digit for 460 as 5. However, if you are using an integer then the division truncates the result, so there is no need to do any rounding at all.
Divide the value and store it back into the same variable.
Use the value after the loop, there is no point in updating it while you still have multiple digits in the variable.
Code:
int test = (int)globalVariableNumber.number;
while (test >= 10) {
test = test / 10;
}
globalVariableNumber.helpMe = test.ToString();
By using Math.Round(), in your example, you're rounding 5.5 to 6 (it's the even integer per the documentation). Use Math.Floor instead, this will drop the decimal point but give you the number you're expecting for this test.
i.e.
double test = Convert.ToDouble(globalVariableNumber.number);
while (test > 10)
{
test = Math.Floor(test / 10);
globalVariableNumber.helpMe = Convert.ToString(firstDigit);
}
Like #Sam Greenhalgh mentions, though, returning the first character of the number as a string will be cleaner, quicker and easier.
globalVariableNumber.helpMe = test >= 10
? test.ToString().SubString(0, 1)
: "Hint not possible, number is less than ten"
This assumes that helpMe is a string.
Per our discussion in the comments, you'd be better off doing it like this:
private void helpButton_Click(object sender, EventArgs e)
{
label3.Text = GetHelpText();
label3.AutoSize = true;
}
// Always good practice to name a method that returns something Get...
// Also good practice to give it a descriptive name.
private string GetHelpText()
{
return test >= 10 // The ?: operator just means if the first part is true...
? test.ToString().SubString(0, 1) // use this, otherwise...
: "Hint not possible, number is less than ten" // use this.
}
Well I enquired about checking if certain keywords can be found in an list and if they are all there the question is correct. Found here: Check if the string contains all inputs on the list
What I would like to also know is how many of the words are in the list, then divide it and get a percentage, so the user knows how accurately they answered each question.
public String KeyWords_Found()
{
int Return_Value = 0;
foreach (String s in KeyWords)
{
if (textBox1.Text.Contains(s))
{
Return_Value++;
}
}
int Holder = Return_Value / KeyWords.Count;
int Fixed = Holder * 100;
return Fixed + "%";
}
So what I want that code it do is check for all instances of keywords listed into the list KeyWords. Then get the percentage by dividing by the total amount of keywords and multiplying by 100. But it says that both values are 0 and i cant divide by 0. I'm not sure why they would be zero. Confused! Help!
You should first check, if KeyWords is empty or not
public String KeyWords_Found()
{
if (KeyWords.Count == 0)
return "0%";
// rest of the code
}
Alternatively you could use Linq instead of writing your own method:
int nOfOccurences = KeyWords.Where(k => textBox1.Text.Contains(k)).Count();
make sure you are using System.Linq; for that to work.
You'll still need to check for KeyWords.Count == 0 and compute the percentage yourself, though.
You should use floating point maths instead of integer maths in your calculations.
int i=100;
int a=51;
(i/a)==0 //true, integer division sucks for calculating percentages
((double)i/a)==0 //false, actually equals ~1.96
Are there any C# libraries out there that provide the same kind of functionality google does when you type in a query such as "13 miles 743 yards in meters" it will return "21 600 meters" (for example).
What I want to be able to do is give a function the string part 13 miles 743 yards and it spits back an int/double with the given distance in meters. It needs to be able to handle all unit input types (kilometers/meters/furlongs/miles/yards/...) but the output only has to be in meters.
It isn't that hard to write my own, but it would be great to just have a tested library ready to go.
I couldn't find any answer to this, so I built my own :) The only real 'magic' here is the Regex expression to grab the groups of values/units out of the original string. From there it's simple fraction/number parsing and then working out how many meters each unit represents. I have not tested this much at all, so please let me know if you find improvements or bugs (the code below should throw an exception when it can't handle a situation).
It won't handle stupid user input, but provided the format of each section is "[number] [unit]" I think it should work fine. There is not much you can assume if the input doesn't conform (e.g., 12/32/43 or 1.43.3.2.44 as a value) anyway. I think it will handle extra fluff in the sentence too such as 1 kilometer and 10 miles (will strip out the and). I haven't added every unit possible, if you know of a complete list of units & there meter equivalent I would love to know about it.
Here are a couple tests,
var a = ExtractDistance("1 1/16 Miles 3/4 yards");
var b = ExtractDistance("02234890234.853 meters");
var c = ExtractDistance("1.8 miles 3.2 furlong");
var d = ExtractDistance("1 kilometer");
var e = ExtractDistance("1/16 Miles");
and here is my code:
private static Dictionary<string, double> _DistanceLookup = new Dictionary<string, double>()
{
{"mile", 1609.344},
{"furlong", 201.168},
{"yard", 0.9144},
{"inch", 0.0254},
{"foot", 0.3048},
{"feet", 0.3048},
{"kilometer", 1000},
{"kilometre", 1000},
{"metre", 1},
{"meter", 1},
{"centimeter", 0.01},
{"centimetre", 0.01},
{"millimeter", 0.001},
{"millimetre", 0.001},
};
private static double ConvertFraction(string fraction)
{
double value = 0;
if (fraction.Contains('/'))
{
// If the value contains /, we need to work out the fraction
string[] splitVal = fraction.Split('/');
if (splitVal.Length != 2)
{
ScrewUp(fraction, "splitVal.Length");
}
// Turn the fraction into decimal
value = double.Parse(splitVal[0]) / double.Parse(splitVal[1]);
}
else
{
// Otherwise it's a simple parse
value = double.Parse(fraction);
}
return value;
}
public static double ExtractDistance(string distAsString)
{
double distanceInMeters = 0;
/* This will have a match per unit type.
* e.g., the string "1 1/16 Miles 3/4 Yards" would have 2 matches
* being "1 1/16 Miles", "3/4 Yards". Each match will then have 4
* groups in total, with group 3 being the raw value and 4 being the
* raw unit
*/
var matches = Regex.Matches(distAsString, #"(([\d]+[\d\s\.,/]*)\s([A-Za-z]+[^\s\d]))");
foreach (Match match in matches)
{
// If groups != 4 something went wrong, we need to rethink our regex
if (match.Groups.Count != 4)
{
ScrewUp(distAsString, "match.Groups.Count");
}
string valueRaw = match.Groups[2].Value;
string unitRaw = match.Groups[3].Value;
// Firstly get the value
double value = 0;
if (valueRaw.Contains(' '))
{
// If the value contains /, we need to work out the fraction
string[] splitVal = valueRaw.Split(' ');
if (splitVal.Length != 2)
{
ScrewUp(distAsString, "splitVal.Length");
}
// Turn the fraction into decimal
value = ConvertFraction(splitVal[0]) + ConvertFraction(splitVal[1]);
}
else
{
value = ConvertFraction(valueRaw);
}
// Now work out based on the unit type
// Clean up the raw unit string
unitRaw = unitRaw.ToLower().Trim().TrimEnd('s');
if (!_DistanceLookup.ContainsKey(unitRaw))
{
ScrewUp(distAsString, "unitRaw");
}
distanceInMeters += value * _DistanceLookup[unitRaw];
}
return distanceInMeters;
}
private static void ScrewUp(string val, string prop)
{
throw new ArgumentException("Extract distance screwed up on string [" + val + "] (bad " + prop + ")");
}
Enjoy! I hope someone out there finds this useful. Please leave comments/suggestions.
EDIT: added a , to the regex string to handle 1,300 meters style format
One way to do it is to make a request to google then parse the returned html.
Update: This will be really inefficient, but they have taken care of the hard work for you. To make this work, you would have to make a English (for your example) language parser to take the input, strip out the meaningless words/symbols (like and and commas), find the values (13 and 743), find the units (miles, yards, and meters), find the operators (in or to). After that, you have to make sure that it makes syntactical sense. You also have to keep a table of conversions (not hard).
It's definitely possible, but it's a bunch of work, and I'm not sure if one exists already (other than google). There are so many corner cases you have to worry about. Making a library to do the work would be a fun exercise, but it is difficult to catch all cases.
The easier solution would to be give them discrete controls to take the language parsing out
Here is a unit conversion library. Doesn't have all of your desired units of measurement (furlongs!?) but looks to have most:
http://www.codeproject.com/KB/library/Measurement_Conversion.aspx
Haven't found anything with the string parsing. Honestly that seems like an error prone way to get input. Consider:
13 miles and 743 yards in meters
13 miles 743 yards to meters
13 miles and 743 yards to
meters
All mean the same thing and even if you give painfully specific instructions about how to write out their string they will probably do what makes sense to them...
If you want to get into what people are TRYING to say then you really might be better off going with Google. Otherwise you might try fielding the specific inputs.