# HP Forums

Full Version: WP 34s: Trying to understand SLV
You're currently viewing a stripped down version of our content. View the full version with proper formatting.

Hi,

I am having a hard time with SLV. I wanted to transfer the 15c Cash flow analysis to the 34s (Advanced functions handbook) but, even if the routine calculating NPV gives the expected result for every example I could find, when the same routine is called through SLV to solve IRR as with the 15C, the results are just off.

Trying other examples from the 15c manual, I can see that the 34s solver sometimes should use different initial guesses to obtains result or not show a Solve failed message (page 184, section 13 on the original 15c owner handbook).

What should I consider to make SLV to give better results? I really would like to port the CFA to the 34s.

Thanks,

Miguel

Thanks

The 34S solver is a completely different implementation to the 15c. For starters, it is using a quadraticly converging approximation which should converge much faster for many functions -- you'll see this in the 2nd best estimate which is often many digits less accurate unlike the same on the 15c.

The 34S solver also solves to the display resolution -- so e.g. FIX 02 will give a very rough estimate, FIX 08 better etc. The 15c solver doesn't depend on the display settings. Conversely, the 34S integration doesn't depend on the display settings and the 15c's does.

- Pauli

Hi Pauli,

Is there a way to make this work with the 34s solver? This is an adaptation of the routine found on the 15c advanced functions handbook. The program works for NPV but, as I mentioned, the IRR does not get it right.

For IRR there are two examples on page 43 and 44 that should yield 8.04 and 10.06 respectively.

if, as written, this is not possible, is there another formula that I could use to calculate IRR?

Thanks,

Miguel

```001 LBL'CFE'		// Cash flows entry
002 CL[alpha]
003 2
004 ENTER[^]
005 EEX
006 5
007 /
008 STO K
009 [alpha]'AMT'
010 [alpha] [^]
011 [alpha]'OCC'
012 LBL 00
013 CLx
014 PROMPT
015 x=0?
016 GTO 01
017 [cmplx]STO[->]K
018 ISG K
019 NOP
020 GTO 00
021 LBL 01
022 RCL K
023 IP
024 x=0?
025 RTN
026 2
027 -
028 EEX
029 2
030 [times]
031 2
032 +
033 EEX
034 5
035 /
036 STO K
037 RTN
038 LBL'NPV'		// Calls label 02 to calculate Net Present Value
039 SSIZE4
040 CL[alpha]
041 [alpha]'Int'
042 [alpha] ?
043 PROMPT
044 EEX
045 2
046 /
047 STO I
048 RCL K
049 STO J
050 XEQ 02
051 RTN
052 LBL'IRR'		// Solves label 02 to calculate IRR
053 SSIZE4
054 RCL K
055 STO J
056 EEX
057 3
058 +/-
059 STO I
060 SLV 02
061 GTO 10
062 CL[alpha]
063 [alpha]'No[space]'
064 [alpha]'sol'
065 [alpha]'uti'
066 [alpha]'on!'
067 [alpha]VIEW
068 RTN
069 LBL 10
070 EEX
071 2
072 [times]
073 RTN
074 LBL 02
075 RCL I
076 1
077 STO A
078 +
079 STO B
080 0
081 STO C
082 LBL 03
083 [cmplx]RCL[->]J
084 RCL I
085 x=0?
086 GTO 04
087 1
088 +
089 x[<->] Y
090 +/-
091 y[^x]
092 STO A
093 1
094 x[<->] Y
095 -
096 RCL/ I
097 RCL[times] B
098 GTO 05
099 LBL 04
100 R[v]
101 LBL 05
102 [times]
103 STO+ C
104 RCL A
105 STO[times] B
106 ISG J
107 GTO 03
108 RCL C
109 RTN
```

There are several reasons why your program does not work.

1. SLV expects the current value of the variable to solve for in X. On each function call (here label 02) it fills the stack with the interest rate and expects the routine to use this value as the currect guess for which the NPV (that should become zero) is calculated. So you have to store this value first (LBL 02  STO I) before the NPV is evaluated. Also setting the loop control number (RCL K  STO J) should generally be handled here.

In my program version I chose to hand over the interest rate as a percentage (i.e. i instead of i/100) and use the following steps at Lbl 02. This way the NPV routine may be placed directly before:

```  LBL "NPV"
"I%?"
PROMPT
SSIZE 4
LBL 02
1
STO A
x<>y
%       ; i/100
STO I
+       ; 1 + i/100
STO B
CLX
STO C
RCL K
STO J
LBL 03
...
```
2. SLV expects two (!) initial guesses for the interest rate in X and Y. In your program, one of them is 0,001 (i.e. 0,1%) and the other is the value that happens to be in Y before calling SLV - this is the control number stored in K. #-)

Give SLV two decent guesses and it will work. For instance 0 and 1 for 0 and 100%. In my program (with i instead of i/100) I used 0 and 100. This will work in most (!) cases, but not in all - providing two good guesses here is a work of art since the interest rate can also be negative. ;-)

3. SLV is quite picky at judging its own results. It may throw an error even though the result is fine. This especially happens in ALL mode (or other modes displaying many digits). So use something like FIX 6 instead which will return the (decimal) IRR with six digits after the decimal point or four digits for the percentage. Finally switch back to FIX 2 or something similar. I did it this way and it worked for me:
```  0        ; 1st guess = 0%
FIX 06   ; results correct to six decimals will do
EEX      ; 2nd guess = 100% - use 1 in your program instead
2
SLV 02   ; Launch Solve
X~~Y?    ; check if both last results agree in displayed digits
GTO 10   ; in this case the result is fine as well, so exit
ERR 20   ; otherwise throw an error
LBL 10   ; exit and display result
FIX 02   ; reset display to usual 2 digits
...
```

Pauli, there might be a problem with the SLV routine since I sometimes got an "solve failed" error although both final approximations agreed in all 16 digits. However, the final function value was not zero, which in these cases still was fine because the function usually returned values order of 10^6 and larger. On the other hand the returned value might be checked as mentioned above.

Anyway, analyzing the value returned by Solve is essential here because the NPV is calculated in a way that does not ensure the best possible precision. For instance, ((1+i)^n)-1 can be evaluated much more precisely by using e^x-1 and ln(1+x) as offered by the 34s.

4. You should also take a look at the sign of the returned NPV. However, the correct IRR does not depend on this.

5. There is some room for improvement. For instance, in the entry routine simply use INC K twice instead of ISG K  NOP and you can forget the clumsy initial calculation of the loop control number. And instead of your own error message you can simply throw ERR 20 (Solve failed). ;-)

Change the program accordingly and it will work. Just tried it myself - it returns the same values as those in the advanced functions handbook. ;-)

Dieter

Edited: 23 Oct 2011, 3:55 p.m. after one or more responses were posted

Buenas tardes Miguel,

What spontaneously crossed my mind reading your original post: Why doesn't he use a financial calc for this? Honestly, we made the WP 34S (sic!) to be a scientific calc since HP's activities were a bit low in this area.

Though I admit I voted for some %-functions and also a TVM solver many weeks ago, I'm most willing to kick those out if there will be any trouble with them. If you want a financial calc, get the 30b or 17bii+ Silver and you'll be done.

Walter

Thank you very much Dieter, I will make the changes. Your detailed explanation helped me to better understand how SLV works.

And a question: why not to publish yours as an article?

Regards,

Miguel

Edited: 23 Oct 2011, 3:58 p.m.

Hola Walter

Ah! But I want the one to control them all!

Seriously, I always wanted the pocketable, simple calculator that can cope with finances as well as science and maths. And the 34s is what I am finding closest to this.

Thanks,

Miguel

I think this a bit too special for an article, and all information can also be found in the manual (well, a bit hidden here and there as it relies on the reader to know how previous HPs worked ;-)). However, please note I made a final correction in the second code example, about the same time you wrote your reply. I assumed an error that in fact was not there. For the same reason point 4 (sign of returned NPV) is obsolete as well. #-)

Dieter

Edited: 23 Oct 2011, 4:40 p.m.

Dieter

I think your example is a very illuminating demonstration of how SLV works. This is exactly the way that classic HP manuals explain some of the more complicated functions - take an example and implementing.

cheers!

Quote:
Pauli, there might be a problem with the SLV routine since I sometimes got an "solve failed" error although both final approximations agreed in all 16 digits. However, the final function value was not zero, which in these cases still was fine because the function usually returned values order of 10^6 and larger. On the other hand the returned value might be checked as mentioned above.

I know of this problem, I haven't worked out how to fix it. The error is being thrown when the two estimates are equal when rounded but the function evaluation isn't rounding to zero. At least I think this is the cause. The problem is in the keystroke wrapper so should be fixable without using lots of flash.

- Pauli

I thinks this deserves a closer look. Imagine the solver has determined two successive guesses that agree in all displayed (or even all sixteen) digits. But does this also mean that the respective function result rounds to zero as well? Of course it doesn't, depending on the first derivative of the function.

Let us consider a very simple function like this in ALL mode:

```   LBL 01
x^2
7
-
RTN
```
So the solver should return sqrt(7) = 2,64...

Let's see what it comes up with:
```   2 [ENTER] 3
SLV 01
=> Solve FA1LEd
```
What happened here? The solution has a true value of 2,6457 51311 06459 05905... You see that the 16th significant digit is quite exactly right in the middle between 0 and 1. So the 16-digit result returned by the solver has to be ...064591 or maybe ...064590. It doesn't matter which of these two is returned - none of them will provide a function result that rounds to zero (which in this case I assume means "less than 5 E-17"). You either get 2 E-15 or -3 E-15. That's why solve will throw an error although in fact it has found the correct solution.

There is some simple mathematics behind this: if the solution is varied by 1 ULP = 1 E-15 the function result will vary by as much as 2 * sqrt(7) * 1 E-15 = 5,3 E-15. So chances are there simply is no 16-digit value that will return a function result close enough to zero. Try the example modified to sqrt(2), with 1 and 2 as the initial guesses, and even in FIX 4 solve thinks it was unable to find a solution.

Actually solve will almost always throw an error if the display mode is SCI or ENG. This seems to happen every time the function result is not exactly zero. Try the mentioned example in SCI 9 or even SCI 0 mode - you will always get an error message.

That's why I think it does not make much sense if solve checks whether the function result rounds to zero or not. Even the best possible solution may return a result that is significantly different from zero. If you want to include an additional test one might think of varying the result by 1 ULP and see if the function result can be reduced this way. In the IRR example this leads to a slightly better result (in the last digit).

Dieter

For the record: here is your program with a few modifications. It is the same algorithm with a numeric improvement using ln1+x and e^x-1 as well as the changes discussed before. You may use this as a basic version for your own, better and even more reliable program. ;-)

The result of the solver now is checked in three ways. Does the solver think it has found a solution? If not, do the last two guesses agree in their first six decimals? If not, is at least the NPV for this result less than half a cent? If not, throw an error, else display the result.

Since I prefer the hotkeys A...D I used these instead of alpha labels. I also prefer to enter an interest rate and then simply press [B] instead of XEQ"NPV", then being prompted for the interest rate and finally get a result.

So here's my current, unfinished and experimental version. Use it at your own risk. BTW it's the first time I used the (dis)assembler - very nice. ;-)

```001 LBL A
002 SSIZE4
003 CLSTK
004 STO I
005 STO J
006 STO K
007 ALL 03
008 LBL 00
009 CLx
010 CL[alpha]
011 [alpha]'CF'
012 [alpha]IP J
013 [alpha]'[^]n'
014 [alpha]IP J
015 PROMPT
016 x=0?
017 GTO 01
018 STO+ I
019 [cmplx]STO[->]K
020 INC J
021 INC K
022 INC K
023 GTO 00
024 LBL 01
025 RCL K
026 x=0?
027 ERR 15
028 2
029 -
030 EEX
031 3
032 /
033 2
034 EEX
035 +/-
036 5
037 +
038 STO K
039 RCL I
040 CL[alpha]
041 [alpha] #
042 [alpha]'CF'
043 [alpha] =
044 VW[alpha]+ X
045 RTN
046 LBL B
047 SSIZE4
048 XEQ 02
049 FIX 02
050 CL[alpha]
051 [alpha]'NPV'
052 [alpha] =
053 VW[alpha]+ X
054 RTN
055 LBL C
056 SSIZE4
057 FIX 06
058 CLSTK
059 EEX
060 2
061 SLV 02
062 GTO 05
063 x[approx]? Y
064 GTO 05
065 RCL Z
066 FIX 02
067 ROUND
068 x[!=]0?
069 ERR 20
070 R[v]
071 LBL 05
072 FIX 02
073 CL[alpha]
074 [alpha]'IRR'
075 [alpha] %
076 [alpha] =
077 VW[alpha]+ X
078 RTN
079 LBL 02
080 1
081 STO A
082 %
083 STO I
084 STO B
085 INC B
086 CLx
087 STO C
088 RCL K
089 STO J
090 LBL 03
091 [cmplx]RCL[->]J
092 RCL I
093 x=0?
094 GTO 04
095 LN1+x
096 +/-
097 [times]
098 e[^x]-1
099 STO A
100 INC A
101 +/-
102 RCL/ I
103 RCL[times] B
104 ENTER[^]
105 LBL 04
106 R[v]
107 [times]
108 STO+ C
109 RCL A
110 STO[times] B
111 ISG J
112 GTO 03
113 RCL C
114 RTN

A        B        C
Start   i%=>NPV   =>IRR%
```
Here's the example from the 15C Advanced Solutions Handbook p. 43:
```  Keys              Display

[A]               CF0^n0
0.
-80000 [ENTER] 1
[R/S]             CF1^n1
0.
-600 [ENTER] 1
[R/S]             CF2^n2
0.
6500 [ENTER] 1
[R/S]             CF3^n3
0.
8000 [ENTER] 2
[R/S]             CF4^n4
0.
7500 [ENTER] 2
[R/S]             CF5^n5
0.
91000 [ENTER] 1
[R/S]             CF6^n6
0.
[R/S]             #CF=
8.
```
There are 8 cashflows in total.

Now let's determine the NPV for an interest rate of 9%.
```  9   [B]           NPV=
-4108.06
```
Since the NPV is negative the IRR has to be less than 9%.

What is its value?
```      [C]           IRR%=
8.04
```

Dieter

Dieter, it's still a good idea to have an alpha label at the beginning of the program so that it can be found in a flash region. Look at the vectors program in the library folder for an example.

Quote:
BTW it's the first time I used the (dis)assembler - very nice. ;-)

Thank you, Dieter! I appreciate the comment.

Cheers...

Thank you Dieter,

I have already made the changes discussed before and was thinking about improving precision using ln1+x and e^x-1, but you were faster. :-)

I really appreciate these improvements and the time you took to help.

Regards,

Miguel

Edited: 28 Oct 2011, 12:19 p.m.

Hi Marcus,

That was my original intention : to run this and other financial routines I am programing from a flash region. When they are ready, I will post them on an article, for whoever will be interested.

Thanks to Dieter, I finally got how SLV works (it is really different from was I knew with the 35s or 32sII) and I am using the new knowledge a lot. :-)

Regards,

Miguel

Edited: 28 Oct 2011, 3:07 p.m.

I'm not seeing this one fail on the console emulator? What firmware revision are you using?

Probably moot now, I've take the almost zero check out of the solver code. It won't fail anymore if the guesses converge. The user will have to check that a zero was found instead of a discontinuity.

- Pauli

Quote:
Probably moot now, I've take the almost zero check out of the solver code. It won't fail anymore if the guesses converge. The user will have to check that a zero was found instead of a discontinuity.

Looking at the previous and the current XROM code for the solver 'm now really confused! :-(

Here's the previous version:

```slv_fin::		RCL Z
FILL
---			x[approx]0?
---				JMP slv_found
XEQ slv_fixstack
JMP exit
---slv_found::		XEQ slv_fixstack
---			JMP slv_fail_common
```
In your todays change you've remove the 4 lines which I've marked with --- at the beginning.

But if I understand your previous version correctly, then if x[approx]0? it jumps to slv_found, but from there it jumps again to slv_fail_common (which gives an error). Isn't THIS the problem of the previous version?

I guess this x is the function value, but if it's almost zero then the solver didn't fail, correct?

So I would say that the only wrong thing in your old solver version was, that this condition and the jump was wrong - or IOW: why does the label slv_found jump to slv_fail_common ???

Franz

I don't know what I wrote in the old code :-(

It wasn't failing for the case Dieter presented but it wasn't correct either.

- Pauli

Quote:
I don't know what I wrote in the old code :-(

Well, first I would say that the solver should indeed test if the function value is almost zero, and if not then maybe it could give an error message (something like "Dubios accuracy" or "Possible infinity").

Then I would prefer having the solution x in X and the function value f(x) in Y, so a simple x<->y would show if the solution is correct.

And finally I think you should only change your old XROM code as follows (i.e. swap the 2 JMP lines):

```slv_fin::		RCL Z
FILL
x[approx]0?
JMP slv_found
XEQ slv_fixstack
JMP slv_fail_common
slv_found::		XEQ slv_fixstack
JMP exit
```
Franz

Edited: 29 Oct 2011, 7:47 a.m.

I had used the emulator in version 2.2 1630. In the meantime the latest emulator version (wp34s-V22.zip) seems to be 2.2 1782 which I downloaded now. It returns the same results:

```  P/R
LBL 01
x^2
7
-
RTN
P/R

ALL 03

2 ENTER 3
SLV 01

Solve FA1LEd
```

As mentioned, I think a final check for a near-zero-result is not a good idea at all. Take a look at the example. The best function results a 16-digit calculator can come up with are 2E-15 or -3E-15. Is this "approximately zero"? If you think so, consider the same function multiplied with 1E15, i.e. f(x) = 10^15*(x^2-7). In this case the best possible function results are 2 resp. -3. Which sure is not "approximately zero". ;-)

BTW - the other example x^2 - 2 in FIX 4 fails as well in 2.2. 1782.

There is a way to see whether the two last converging guesses actually are a valid solution. The result may be varied by 1 ULP until a sign change occurs. This is similar to the way Solve worked on the 34C and 15C: if the last guess did not return exactly zero they returned two final estimates with opposite signs of the function result, which were returned in Z and T as well. So the user was sure the actual solution had to be somewhere inbetween.

Dieter

Quote:
As mentioned, I think a final check for a near-zero-result is not a good idea at all.

Or do it as I suggested: make this near-zero-check, but don't let the solver fail if it's not zero, but just display a 'warning' message instead (now we even have such a MSG command).

Franz

The solver near zero check was been removed in 1785 so you don't have it yet :-(

In fact I'm pretty disillusioned with the approximately zero test. It seems to be almost useless -- rounding a near zero number generally doesn't change it.

I don't see two function values being documented as being returned in either the 34c or 15c manuals about solve. Unless I've missed something. They both say, best in X next best in Y and f(X) in Z.

- Pauli

Quote:
In fact I'm pretty disillusioned with the approximately zero test. It seems to be almost useless -- rounding a near zero number generally doesn't change it.

I don't see two function values being documented as being returned in either the 34c or 15c manuals about solve. Unless I've missed something. They both say, best in X next best in Y and f(X) in Z.

May well be we know the reason now.

Walter

Fine, so I'll have the modified SLV version once the emulator gets an update. ;-)

Quote:
In fact I'm pretty disillusioned with the approximately zero test. It seems to be almost useless -- rounding a near zero number generally doesn't change it.

I wonder how this test is supposed to work. Is it the same as ROUND followed by X=0? In this case it will only work in FIX mode, since in all other cases the value in X is simply rounded to its displayed number of significant digits. Which never will get zero - unless the unrounded value already is.

Anyway, the test may be useful in programming ...once it has been carefully documented. In FIX n it actually tests if abs(x) is less than 0,5x10-n.

Quote:
I don't see two function values being documented as being returned in either the 34c or 15c manuals about solve. Unless I've missed something. They both say, best in X next best in Y and f(X) in Z.

You are right, I just checked both the 34C manual and the device itself. #-)

However, I still think returning the two function results in Z and T is a good idea.

Dieter

Edited: 30 Oct 2011, 10:55 a.m.

In the meantime I updated the emulator replacing wp34s.exe and wp34sgui.exe with their current versions (as of sunday evening UTC). VERS now shows 2.2 1789. Switching to program mode, 510 available steps are displayed instead of the usual 506. Is this correct?

I then tested the same example (solve x^2 - 7) on this newer version. With the same initial guesses 2 and 3 in ALL mode it still throws the same error: Solve Fa1LEd, although the result is fine and X and Y agree in all digits. In FIX 11 the same result is returned without an error message.

Dieter

Don't rely on the current builds! I'm heavily working on the inner workings and many check-ins are just for safety or cross checking by my fellow developers.

If we are done solve and integrate can be nested to any depth, and unused program space can be used as data. Be patient!

From a user's point of view, we should close v2.2 then at a build number it was sufficiently stable. Then do all the mess in an incremented version.

Quote:
Don't rely on the current builds!

How true!

Today (SVN 1804) I've compiled my program with wp34s_asm.pl and got "Illegal opcode" in the emulator (also SVN 1804). Looking at the program in the flash I saw commands like GTO without any address, or XEQ 1 (single digit) ...

BTW, what's this new LOCL and 200 instead of 100 for the address range?

If you want us to test your new 'ideas' then you should really give us at least a bit of information for such new features. ;-)

Franz

Edited: 1 Nov 2011, 1:14 p.m.

Some kind of "inaccurate" flag sure is a nice idea - but where is the limit? It depends on the function resp. its first derivative and the value of the root SLV has found. So I'd say there should be no final close-to-zero test. Instead a simple trick may be used: have the user round the function result to zero, if desired.

At the same time this is a simple but efficient technique to cancel solve's iteration as soon as the desired accuracy level of the function result is reached. This idea was also used in the IRR example (function result < 0,005? => result ok). If the user really wants a final "approximately zero" check he may simply add X~~0? Clx as the final steps of his function.

Dieter

Send me your file and I'll try it. Read my posting above: I'm working so don't expect that the software is in a usable state. That's why we have a release package that should work.

LOCL is an abbreviation. :-)

Quote:
LOCL is an abbreviation. :-)

WOW, that's really a comprehensive explanation! And since I'm a clever guy I guess it's an abbreviation for LOCAL. ;-)

Now I know what to do with it ...