6

I need to compute sin(4^x) with x > 1000 in Matlab, with is basically sin(4^x mod 2π) Since the values inside the sin function become very large, Matlab returns infinite for 4^1000. How can I efficiently compute this? I prefer to avoid large data types.

I think that a transformation to something like sin(n*π+z) could be a possible solution.

Kirk Broadhurst
  • 27,836
  • 16
  • 104
  • 169
Bene
  • 157
  • 11
  • 1
    You might also like to see [mathoverflow](http://mathoverflow.net/) for questions on maths - though this one does relate to programming. – Jeff Dec 02 '12 at 22:39
  • 3
    I have a hunch that any method would lead to junk due to double precision arithmetic: e.g. computing sin(2*pi*10^i) for i = 1:100 gives junk in Matlab (or C for that matter...it's really a matter of double precision arithmetic), and similarly computing mod(2*pi*10^i + 0.0001, 2*pi) gives junk for i = 1:100. – db1234 Dec 02 '12 at 22:42
  • That is my fear as well. Hence, I usually try to find mathematical solutions before using loops. Thank you. – Bene Dec 02 '12 at 22:48
  • @Jeff this question would be off-topic for mathoverflow, which is for "**research level math questions**, the sorts of questions you come across when you're writing or reading articles or graduate level books". You may be thinking of [math.se], which is for "for people studying mathematics at any level and professionals in related fields" – AakashM Dec 03 '12 at 10:36
  • @AakashM Ah yes, that was what I was thinking of. My bad... – Jeff Dec 03 '12 at 19:32

3 Answers3

13

You need to be careful, as there will be a loss of precision. The sin function is periodic, but 4^1000 is a big number. So effectively, we subtract off a multiple of 2*pi to move the argument into the interval [0,2*pi).

4^1000 is roughly 1e600, a really big number. So I'll do my computations using my high precision floating point tool in MATLAB. (In fact, one of my explicit goals when I wrote HPF was to be able to compute a number like sin(1e400). Even if you are doing something for the fun of it, doing it right still makes sense.) In this case, since I know that the power we are interested in is roughly 1e600, then I'll do my computations in more than 600 digits of precision, expecting that I'll lose 600 digits by the subtractive cancellation. This is a massive subtractive cancellation issue. Think about it. That modulus operation is effectively a difference between two numbers that will be identical for the first 600 digits or so!

X = hpf(4,1000);
X^1000
ans =
114813069527425452423283320117768198402231770208869520047764273682576626139237031385665948631650626991844596463898746277344711896086305533142593135616665318539129989145312280000688779148240044871428926990063486244781615463646388363947317026040466353970904996558162398808944629605623311649536164221970332681344168908984458505602379484807914058900934776500429002716706625830522008132236281291761267883317206598995396418127021779858404042159853183251540889433902091920554957783589672039160081957216630582755380425583726015528348786419432054508915275783882625175435528800822842770817965453762184851149029376

What is the nearest multiple of 2*pi that does not exceed this number? We can get that by a simple operation.

twopi = 2*hpf('pi',1000);
twopi*floor(X^1000/twopi)
ans = 114813069527425452423283320117768198402231770208869520047764273682576626139237031385665948631650626991844596463898746277344711896086305533142593135616665318539129989145312280000688779148240044871428926990063486244781615463646388363947317026040466353970904996558162398808944629605623311649536164221970332681344168908984458505602379484807914058900934776500429002716706625830522008132236281291761267883317206598995396418127021779858404042159853183251540889433902091920554957783589672039160081957216630582755380425583726015528348786419432054508915275783882625175435528800822842770817965453762184851149029372.6669043995793459614134256945369645075601351114240611660953769955068077703667306957296141306508448454625087552917109594896080531977700026110164492454168360842816021326434091264082935824243423723923797225539436621445702083718252029147608535630355342037150034246754736376698525786226858661984354538762888998045417518871508690623462425811535266975472894356742618714099283198893793280003764002738670747

As you can see, the first 600 digits were the same. Now, when we subtract the two numbers,

X^1000 - twopi*floor(X^1000/twopi)
ans =
3.333095600420654038586574305463035492439864888575938833904623004493192229633269304270385869349155154537491244708289040510391946802229997388983550754583163915718397867356590873591706417575657627607620277446056337855429791628174797085239146436964465796284996575324526362330147421377314133801564546123711100195458248112849130937653757418846473302452710564325738128590071680110620671999623599726132925263826

This is why I referred to it as a massive subtractive cancellation issue. The two numbers were identical for many digits. Even carrying 1000 digits of accuracy, we lost many digits. When you subtract the two numbers, even though we are carrying a result with 1000 digits, only the highest order 400 digits are now meaningful.

HPF is able to compute the trig function of course. But as we showed above, we should only trust roughly the first 400 digits of the result. (On some problems, the local shape of the sin function might cause us to lose more digits than that.)

sin(X^1000)
ans =
-0.1903345812720831838599439606845545570938837404109863917294376841894712513865023424095542391769688083234673471544860353291299342362176199653705319268544933406487071446348974733627946491118519242322925266014312897692338851129959945710407032269306021895848758484213914397204873580776582665985136229328001258364005927758343416222346964077953970335574414341993543060039082045405589175008978144047447822552228622246373827700900275324736372481560928339463344332977892008702220160335415291421081700744044783839286957735438564512465095046421806677102961093487708088908698531980424016458534629166108853012535493022540352439740116731784303190082954669140297192942872076015028260408231321604825270343945928445589223610185565384195863513901089662882903491956506613967241725877276022863187800632706503317201234223359028987534885835397133761207714290279709429427673410881392869598191090443394014959206395112705966050737703851465772573657470968976925223745019446303227806333289071966161759485260639499431164004196825

So am I right, and we cannot trust all of these digits? I'll do the same computation, once in 1000 digits of precision, then a second time in 2000 digits. Compute the absolute difference, then take the log10. The 2000 digit result will be our reference as essentially exact compared to the 1000 digit result.

double(log10(abs(sin(hpf(4,[1000 0])^1000) - sin(hpf(4,[2000 0])^1000))))
ans =
      -397.45

Ah. So of those 1000 digits of precision we started out with, we lost 602 digits. The last 602 digits in the result are non-zero, but still complete garbage. This was as I expected. Just because your computer reports high precision, you need to know when not to trust it.

Can we do the computation without recourse to a high precision tool? Be careful. For example, suppose we use a powermod type of computation? Thus, compute the desired power, while taking the modulus at every step. Thus, done in double precision:

X = 1;
for i = 1:1000
  X = mod(X*4,2*pi);
end
sin(X)
ans =
         0.955296299215251

Ah, but remember that the true answer was -0.19033458127208318385994396068455455709388...

So there is essentially nothing of significance remaining. We have lost all our information in that computation. As I said, it is important to be careful.

What happened was after each step in that loop, we incurred a tiny loss in the modulus computation. But then we multiplied the answer by 4, which caused the error to grow by a factor of 4, and then another factor of 4, etc. And of course, after each step, the result loses a tiny bit at the end of the number. The final result was complete crapola.

Lets look at the operation for a smaller power, just to convince ourselves what happened. Here for example, try the 20th power. Using double precision,

mod(4^20,2*pi)
ans =
          3.55938555711037

Now, use a loop in a powermod computation, taking the mod after every step. Essentially, this discards multiples of 2*pi after each step.

X = 1;
for i = 1:20
  X = mod(X*4,2*pi);
end
X
X =
          3.55938555711037

But is that the correct value? Again, I'll use hpf to compute the correct value, showing the first 20 digits of that number. (Since I've done the computation in 50 total digits, I'll absolutely trust the first 20 of them.)

mod(hpf(4,[20,30])^20,2*hpf('pi',[20,30]))
ans =
          3.5593426962577983146

In fact, while the results in double precision agree to the last digit shown, those double results were both actually wrong past the 5th significant digit. As it turns out, we STILL need to carry more than 600 digits of precision for this loop to produce a result of any significance.

Finally, to fully kill this dead horse, we might ask if a better powermod computation can be done. That is, we know that 1000 can be decomposed into a binary form (use dec2bin) as:

512 + 256 + 128 + 64 + 32 + 8
ans =
        1000

Can we use a repeated squaring scheme to expand that large power with fewer multiplications, and so cause less accumulated error? Essentially, we might try to compute

4^1000 = 4^8 * 4^32 * 4^64 * 4^128 * 4^256 * 4^512

However, do this by repeatedly squaring 4, then taking the mod after each operation. This fails however, since the modulo operation will only remove integer multiples of 2*pi. After all, mod really is designed to work on integers. So look at what happens. We can express 4^2 as:

4^2 = 16 = 3.43362938564083 + 2*(2*pi)

Can we just square the remainder however, then taking the mod again? NO!

mod(3.43362938564083^2,2*pi)
ans =
          5.50662545075664

mod(4^4,2*pi)
ans =
          4.67258771281655

We can understand what happened when we expand this form:

4^4 = (4^2)^2 = (3.43362938564083 + 2*(2*pi))^2

What will you get when you remove INTEGER multiples of 2*pi? You need to understand why the direct loop allowed me to remove integer multiples of 2*pi, but the above squaring operation does not. Of course, the direct loop failed too because of numerical issues.

  • It is not a beautiful solution. However it works and the performance is ok. Thank you. – Bene Dec 03 '12 at 00:09
  • The problem is, the mod computation for a periodic function is a killer here. Mods for integers are easy, since there is no loss. But mod with respect to 2pi is not so easy. One of my goals when I wrote HPF was to compute sin(1e400). double(sin(hpf('1e400',1000)))=-0.998538231983098. That value is correct. –  Dec 03 '12 at 00:25
  • It looks to me like you've actually computed `sin(4^4) ~ -0.9992`. `sin(4^1000) ~ -0.19033`, I think. – DSM Dec 03 '12 at 00:40
  • @DSM - oops. I was playing around, and copied the wrong result in. Fixed now. –  Dec 03 '12 at 00:44
5

I would first redefine the question as follows: compute 4^1000 modulo 2pi. So we have split the problem in two.

Use some math trickery:

(a+2pi*K)*(b+2piL) = ab + 2pi*(garbage)

Hence, you can just multiply 4 many times by itself and computing mod 2pi every stage. The real question to ask, of course, is what is the precision of this thing. This needs careful mathematical analysis. It may or may not be a total crap.

Pavel Radzivilovsky
  • 18,794
  • 5
  • 57
  • 67
  • I have a hunch that this method, or any method would lead to junk due to double precision arithmetic: e.g. computing sin(2*pi*10^i) for i = 1:100 gives junk, and similarly with computing mod(2*pi*10^i + 0.0001, 2*pi) – db1234 Dec 02 '12 at 22:40
  • yes.. possible. One needs to be careful with machine arithmetics. – Pavel Radzivilovsky Dec 02 '12 at 22:43
  • 1
    I found a mod function for powers and large numbers: http://www.mathworks.com/matlabcentral/fileexchange/7908-big-modulo-function/content/bigmod.m This could be a working solution. – Bene Dec 02 '12 at 22:44
  • See my answer. I show that the procedure suggested here results in crapola. –  Dec 03 '12 at 00:09
3

Following to Pavel's hint with mod I found a mod function for high powers on mathwors.com. bigmod(number,power,modulo) can NOT compute 4^4000 mod 2π. Because it just works with integers as modulo and not with decimals.

This statement is not correct anymore: sin(4^x) is sin(bidmod(4,x,2*pi)).

Bene
  • 157
  • 11
  • I don't trust this. On Octave (open source version of Matlab) I tried to use the bigmod.m routine by typing `sin(2*pi*bigmod(4, 100, 2*pi))` and the result was not zero. I am fairly certain that this would be the case in Matlab too. – db1234 Dec 02 '12 at 23:03
  • 1
    @dblazevski Unless I'm drastically mistaken, it's not supposed to be zero... `4^100 mod 2pi` isn't an integer. – Danica Dec 02 '12 at 23:06
  • Opps...foolish mistake, thanks @Dougal, I guess a test would be `sin(bigmod(4*2*pi, 100, 2*pi))`, and that gives zero as it is supposed to do – db1234 Dec 02 '12 at 23:08
  • 1
    @Bene, not sure if you're interested in non-integer x, but note that bigmod(a, x, m) does not seem to work for non-integer x: `bigmod(4*2*pi + 0.001, 1.123, 2*pi)` is not giving the same result as `mod((4*2*pi + 0.001)^1.123, 2*pi)`. This is even for small values of x. I imagine that something like `bigmod(4, 100.132, 2*pi)` may not be accurate due to this ovservation. – db1234 Dec 02 '12 at 23:16
  • 1
    BTW, I finally got around to explaining (in my answer) why a bigmod computation fails for non-integer arguments. –  Dec 07 '12 at 19:26