4 ms·
1) I posted my code as ` total = principal * (1+((rate/100)/t)) ^ (n*t)`, and you claimed your 10 line "My standard, generalized, library routine is equally bri
by SideQuark 4y ago
1) I posted my code as ` total = principal * (1+((rate/100)/t)) ^ (n*t)`, and you claimed your 10 line "My standard, generalized, library routine is equally brief". By "equally brief" did you mean an order of magnitude larger?
2) Your code and example does not work using 64 bit integers. Using only 64 bit integer math, your code gives 11605682736, off by 90% (easily checked in C/C++/C#). Your math overflows, so you're not using scaled integers; in order to work, your code requires arbitrary sized integers for the loop. Did you not realize this? So you're not using scaled integers as int64s. You're using unbounded sized integers. Your (P*R) term repeatedly overflows a 64 bit int when calculating your example.
3) If you're going to use arbitrary sized integers, then simply compute the exact numerator and denominator, then divide. It's actually accurate, as opposed to your mess. And it's simple to code.
4) You claim "a negative result indicates an overflow failure", which is wrong, since it can overflow twice (or more) in the same calculation, and, since you're using arbitrary sized integers internally, the conversion depends on non-portable behavior. Both of these can be demonstrated quite easily.
5) You claim "floating point math provides no warning when the accuracy of the mantissa is exceeded," which is wrong - it marks it with an Infinty using IEEE 754 math, required by a significant amount of languages, and provided on all major pieces of hardware), which will never revert as your error code can. And for someone understanding numerics (and condition numbers), the calculation is easy to extend with the accuracy of the result so the used will know error bounds.
6) Your code is amazingly slow: your int64 version (which fails almost all tests) is ~6000x slower than the pure double version, and the arbitrary sized int one is 45,000x slower (C#, Release build, tests run over the examples below).
7) Examples where your algorithm fails. Inputs are numbers that occur in real mortgages: interest 18.45% happened in the 1980s, 50, 75, 100 year terms exist (50 is getting common in CA), 24 payments represents bi-weekly payoffs (and some places compound daily, or 365 times a year [1]). To test accuracy of even your int64 routine, lower principal amounts are below to show both your routines (the honest int64 and the arbitratry integer one) still fail. "My routines" are the double one above and, since you require arbitrary sized integers, I'll test one that simply computes exact numerator and denominator then divides:
10000,18450,365,50 -> both your int64 and arbitrary precision are off by $10.08, my routines are both perfect.
Want a lower interest rate?
10000,8000,365,50 -> both of yours are off by $0.12, both mine are still perfect.
Let's push them with a longer term, up the principal:
100000,8000,365,100 -> both yours are off by $6.72, both mine are perfect
Now, since your int64 version pukes immediately on larger numbers, let's only look at the arbitrary sized versions.
500000,8000,365,100 -> yours off by $0.68, mine perfect
Maybe the problem is the daily compounding?
100000000,18450,12,50 -> yours off by $0.03, mine perfect
100000000,18450,12,100 -> yours off by $0.34, mine perfect
And for fun, let's look at a $100k loan, 15%, compounded hourly (say 8760 times a year) like a bad credit loan, for 25 years.... (Note your routine is stupidly slow for this one):
100000,15000,8760,25 - both yours are off by $1.21, both mine are correct.
I can keep producing errors all over this frontier.
8) So, to avoid "comparison decoherence" (!?) because you don't understand floating point (which can be made bit exact, as I've done on many projects for binary file format interop), you instead produce demonstrably buggy, numerically faulty, slow, memory unbounded code?
This is why people should not take advice on numerics from someone that does not understand numerics.
[1] https://www.rocketmortgage.com/learn/compound-interest https://www.rocketmortgage.com/learn/compound-interest
- jqpabc123 4y agoPost your code and I'll fix it for you.
- SideQuark 4y agoOh good, that will be fun. Here's C++ code #include <iostream> #include <cstdint> int64_t scaleFV(int64_t P, int64_t R, int64_t C, int64_t Y) { int64_t SP = 10000; // temporary principal scale factor, 4 decimals if (P > SP * 1000) SP *= 10; // extend accuracy for larger amounts int64_t SR = 1000; // annual rate scale factor, 3 decimals (pre-applied to R) int64_t N = C * Y; // total number of compounding periods int64_t D = C * 100 * SR; // period rate divisor P = P * SP; // scale the principal (int64_t) while (N > 0) { std::cout << P * R << std::endl; // watch the errors fly! P = P + ((P * R) / D); // compound principal with period interest N--; // count the period } return (P + SP / 2) / SP; // unscale result, round back to nearest penny } int main() { std::cout << scaleFV(10000000000L, 8000, 12, 30) << " = 11605682736 != 109357296578\n" << std::endl; } I added a print in your loop to show you the overflow. Good luck. To help you, P * R for this calculation gets about 4x as big as an int 64 can hold. Another way to see it is your answer 109357296578, times your scaling 100000, requires P to hold > 53 bits. But your rate of 8000 is almost 13 bits. So P * R cannot fit in a 64 (well 63 for signed) int. If you cannot fix it, care to explain how your code works only with int64 as you claim here and in other threads on this page? Maybe then you can address the errors I listed above where your routine failed?
- jqpabc123 4y agoIf you cannot fix it, care to explain how your code works only with int64 as you claim here and in other threads on this page? Yes, your code pukes all over itself. And mine doesn't. Why? For more than 2 decades now, Intel processors have included SSE "extensions" with a whole bank of 128 bit registers (XMM0 thru XMM15) with specialized math instructions for integer and floating point. https://en.wikipedia.org/wiki/Streaming_SIMD_Extensions https://en.wikipedia.org/wiki/Streaming_SIMD_Extensions The compiler I use emits SSE opcodes by default for operations on 64 bit integers when building 64 bit executables. In other words, 128 bit processor registers are being used for the calculations. Overflow occurs when the final resultant is too large for int64. So there you have it.