tags:

views:

213

answers:

7

I receive an integer that represents a dollar amount in fractional denominations. I would like an algorithm that can add those numbers without parsing and converting them into doubles or decimals.

For example, I receive the integer 50155, which means 50 and 15.5/32 dollars. I then receive 10210 which is 10 and 21/32 dollars. So 50 15.5/32 + 10 21/32 = 61 4.5/32, thus:

50155 + 10210 = 61045

Again, I want to avoid this:

int a = 50155;
int b = a / 1000;
float c = a % 1000;
float d = b;
d += c / 320f;
// d = 50.484375

I would much prefer this:

int a = 50155;
int b = 10210;
int c = MyClass.Add(a.b); // c = 61045
...
public int Add(int a, int b)
{
    // ?????
}

Thanks in advance for the help!

+1  A: 

Looks like a strange encoding to me.

Anyway, if the format is in 10-base Nxxx where N is an integer denoting whole dollars and xxx is interpreted as

(xxx / 320)

and you want to add them together, the only thing you need to handle is to do carry when xxx exceeds 320:

int a = ..., b = ...; // dollar amounts
int c = (a + b); // add together
// Calculate carry
int carry = (c % 1000) / 320; // integer division
c += carry * 1000;
c -= carry * 320;
// done

Note: this works because if a and b are encoded correctly, the fractional parts add together to 638 at most and thus there is no "overflow" to the whole dollars part.

antti.huima
+4  A: 

Well I don't think you need to use floating point...

public static int Add(int a, int b)
{
    int firstWhole = a / 1000;
    int secondWhole = b / 1000;
    int firstFraction = a % 1000; 
    int secondFraction = b % 1000;
    int totalFraction = firstFraction + secondFraction;
    int totalWhole = firstWhole + secondWhole + (totalFraction / 320);
    return totalWhole * 1000 + (totalFraction % 320);
}

Alternatively, you might want to create a custom struct that can convert to and from your integer format, and overloads the + operator. That would allow you to write more readable code which didn't accidentally lead to other integers being treated as this slightly odd format.

EDIT: If you're forced to stick with a "single integer" format but get to adjust it somewhat you may want to consider using 512 instead of 1000. That way you can use simple mask and shift:

public static int Add(int a, int b)
{
    int firstWhole = a >> 9;
    int secondWhole = b >> 9;
    int firstFraction = a & 0x1ff
    int secondFraction = b & 0x1ff;
    int totalFraction = firstFraction + secondFraction;
    int totalWhole = firstWhole + secondWhole + (totalFraction / 320);
    return (totalWhole << 9) + (totalFraction % 320);
}

There's still the messing around with 320, but it's at least somewhat better.

Jon Skeet
Thanks mate, real good answer, but by my testing my solution is two times faster. Too much modulo arithmatic I think. Any other ideas?
Steve H.
If you want to avoid division, you can use a bitwise right shift.
David Lively
@Steve H: Which solution? You've only shown a partial implementation using floats. You could use Math.DivRem which *might* make it faster... but is this really a bottleneck in the first place? Note that if you can use a custom struct, you may be able to do a lot of the operations without doing the multiplication and remainder nearly as often.
Jon Skeet
@Skeet: My solution which I have not shown which I am comparing yours to for speed. You are correct, this is not the largest bottle-neck but in my industry there are performance benchmarks that must be hit and things I do in microseconds that others do in milliseconds. Just assume I am asking the questions "what is the fasted way to do this?"
Steve H.
@Steve H: I believe that performance is important for you - but what proportion of your time is currently spent with these operations? Is there any reason you can't use a saner data representation which would make it faster overall as well?
Jon Skeet
@Skeet: These operations are my bread and butter. Price comparisons may not take very long, but I do millions of them every second. It is very important this this not be a struct or class because of the performance hit taken by instantiating classes, winding and unwinding the call stack, etc... That's why I'm trying to keep it native, microseconds matter.
Steve H.
One thing nobody's pointed out: If you use floats, you risk floating point error affecting your results. Is it more important that it be fast, or _correct_?
Nick Johnson
@Steve H: You still haven't commented on using a better data structure to start with, rather than banging the two into a single int by multiplying by 1000. Why not multiply by 1024 instead? Then you can use mask and shift operations.
Jon Skeet
@Skeet: I guess to answer that question I need an example of a saner, better data structure. I can only receive the data as an int (50120), string ("50120"), or decimal (50.375).
Steve H.
@Steve H: But do you have to *keep* it in that form? Do you just perform a single operation and then you're done, or are you doing lots of operations with the same values? If it's the latter, then a single conversion to the sane form, then lots of fast operations, then a conversion back to the required form would help things along.
Jon Skeet
@Skeet: No, I do only one operation until it changes again. So it is not really worth converting it to anything at all, and that operation needs to be very fast.
Steve H.
@Skeet: Your second int add() doesn't even work mate, even after I changed second whole to b >> 9...
Steve H.
@Steve: Could you say in what way it doesn't work? Obviously it will give different results because it's a different format - but it should be consistent with that new format. Do you have an example of it failing?
Jon Skeet
+3  A: 

Break the string up in the part that represents whole dollars, and the part that represents fractions of dollars. For the latter, instead of treating it as 10.5 thirty-seconds of a dollar, it's probably easier to treat it as 105 three hundred and twentieths of a dollar (i.e. multiply both by ten to the numerator is always an integer).

From there, doing math is fairly simple (if somewhat tedious to write): add the fractions. If that exceeds a whole dollar, carry a dollar (and subtract 320 from the fraction part). Then add the whole dollars. Subtraction likewise -- though in this case you need to take borrowing into account instead of carrying.

Jerry Coffin
A: 

Hi

If you insist on working in ints you can't solve your problem without parsing -- after all your data is not integer. I call into evidence the (so far) 3 answers which all parse your ints into their components before performing arithmetic.

An alternative would be to use rational numbers with 2 (integer) components, one for the whole part, and one for the number of 320ths in the fractional part. Then implement the appropriate rational arithmetic. As ever, choose your representations of data carefully and your algorithms become much easier to implement.

I can't say that I think this alternative is particularly better on any axis of comparison but it might satisfy your urge not to parse.

Regards

Mark

High Performance Mark
It can also come in string.
Steve H.
+2  A: 

As a point for learning, this representation is called "fixed point". There are a number of implementations that you can look at. I would strongly suggest that you do NOT use int as your top level data type, but instead create a type called Fixed that encapsulates the operations. It will keep your bug count down when you mistakenly add a plain int to a fixed point number without scaling it first, or scale a number and forget to unscale it.

plinth
Learning something new every day. Thanks plinth. :)
Steve H.
+3  A: 

Edit:
This answer suggests that one "stays away" from float arithmetic. Surprisingly, the OP indicated that his float-based logic (not shown for proprietary reasons) was twice as fast as the integer-modulo solution below! Comes to show that FPUs are not that bad after all...

Definitively, stay away from floats (for this particular problem). Integer arithmetic is both more efficient and doesn't introduce rounding error issues.

Something like the following should do the trick
Note: As written, assumes A and B are positive.

int AddMyOddlyEncodedDollars (int A, int B) {
  int sum;
  sum = A + B
  if (sum % 1000 < 320);
     return sum
  else
     return sum + 1000 - 320;
}

Edit: On the efficiency of the modulo operator in C
I depends very much on the compiler... Since the modulo value is known at compile time, I'd expect most modern compilers to go the "multiply [by reciprocal] and shift" approach, and this is fast.
This concern about performance (with this rather contrived format) is a calling for premature optimization, but then again, I've seen software in the financial industry mightily optimized (to put it politely), and justifiably so.

mjv
Oooo I like this... Off the top of my head I don't know modulo arithmatic speed - you?
Steve H.
Sorry mate, I love this solution and it works really well, but modulo, in my testing for my purpose, is two times slower than my solution. :( Thanks though.
Steve H.
@Steve H. I read the comments in the question; glad it confirmed my intuition it might be for the financial world. I'll edit my response to indicate that my weariness about floating arithmetic was wrongly placed. Indeed, FPUs are mighty fast, and obviously we all have to learn from financial hackers...
mjv
"Integer arithmetic is both more efficient and doesn't introduce rounding error issues" - Fixed point does indeed introduce rounding errors. They're more predictable than IEEE floating point, but they're still there.
plinth
How would fixed point introduce rounding errors? It can overflow, sure, but apart from that it is always precise.
mafutrct
@echorhyn the rounding errors were in reference to floating point arithmetic where 2.0 + 2.0 sometimes make 3.99999 depending on what other values were added...
mjv
A: 

BEWARE: This post is wrong, wrong, wrong. I will remove it as soon as I stop feeling a fool for trying it.

Here is my go: You can trade space for time.

Construct a mapping for the first 10 bits to a tuple: count of dollars, count of piecesof32. Then use bit manipulation on your integer:

  • ignore bits 11 and above, apply map.
  • shift the whole number 10 times, add small change dollars from mapping above
  • you now have the dollar amoung and the piecesof32 amount
  • add both
    • move overflow to dollar amount

Next, to convert back to "canonical" notation, you need a reverse lookup map for your piecesof32 and "borrow" dollars to fill up the bits. Unshift the dollars 10 times and add the piecesof32.

EDIT: I should remove this, but I am too ashamed. Of course, it cannot work. I'm so stupid :(

The reason being: shifting by 10 to the right is the same as dividing by 1024 - it's not as if some of the lower bits have a dollar amount and some a piecesof32 amount. Decimal and binary notation just don't split up nicely. Thats why we use hexadecimal notation (grouping of 4 bits). Bummer.

Daren Thomas
Hm, interesting idea. Example to follow?
Steve H.
I am working on an example - and failing. Learning a lot about numbers as I go :)
Daren Thomas