Showing posts with label bc basic calculator. Show all posts
Showing posts with label bc basic calculator. Show all posts

Saturday, September 24, 2016

Calculating π (pi) in C

Maybe I need a better hobby.

Or maybe math really is this fun.

Okay, where this came from -- I gave my son a challenge, and he wrote a short program in perl to calculate π by the area of a circle.

So I wrote one in my favorite calculator, bc.

Now, its a nice program, and I can check the accuracy with bc itself, using the arctangent function. But bc is not as fast as C, and this old intel processor is not that fast, either.

And the program had a bug. Well, in my original explanation, I forgot to mention the problem of rounding. Bc always rounds down. Or, rather, it just drops fractional bits past the current scale This produced some notable effects, which are partially resolved in bc by doubling the scale.

Perhaps one reason bc is not as well known as it might be is that bc doesn't help you with rounding.

And it gives you scale instead of precision or accuracy.

So the limits of computers bite you pretty quick with bc, where other similar solutions help you ignore the limits -- sometimes until it's too late.

Which is part of the reason I'm writing this rant.

Anyway, I also wrote a nice little program for calculating π in standard C, using the language's ordinary math. It uses the same basic approach as I used in the bc version, except that the bc version I showed has a few more bells and whistles.

(I actually started with a very minimalistic version in bc, then wrote the C program and uploaded it to the pastebin linked above, then added the comments and extra features to the bc version before uploading it to a pastebin, if you are wondering why.)

So, if you want a full explanation of the method C source, look at my rants on the bc version (linked above) and the comments in the bc source. I'll limit my comments here to details of C programming and the standard C run-time environment, and issues of rounding and accuracy.

(Modifying the C source to do midpoint and so on is something I leave for you to play with.)

Here's the source of the standard C program, with extra comments:


/* Computing the value of pi by integrating the area of a (half of a) circle.
// by Joel Matthew Rees, 1 August 2013
// Copyright Joel Matthew Rees
//
// Fair use acknowledged.
//
// All rights to this expression of the method reserved.
//
// (Starting from scratch isn't that hard,
// and you'll like the results better.)
// -- Really, writing small programs like this from scratch
// is an excellent way to learn the basic grammar and runtime
// of a programming language. DIY!
// 
//
// Compile with the right libraries:
// cc -Wall -lm -o halfpiArea halfpiArea.c
//
// C language programs have to be compiled. You can't just run them.
// You have to save the source as halfpiArea.c
// and then run the compiler command above in a shell:
// http://joels-programming-fun.blogspot.com/2013/08/get-shell.html
//
// The compiler will catch simple errors in the source,
// and try to give you hints on how to fix them.
// Learning how to read the hints is a topic for another rant or more.
//
// If the compiler succeeds, the command line above will save
// the executable program in a file called halfpiArea,
// which you then run by typing 
// ./halfpiArea
// in the shell.
//
//
*/


/* The following lines help the compiler to figure out
// what functions you are using from the standard library
// and how you should be using them:
*/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
/* They literally include information about those functions
// into this program.
*/


/* The main function of a C program in a standard runtime environment
// is pretty much always called "main()".
// And it has two or three standard arguments.
// I only tell the program about two:
// argc is the integer count of the command line arguments,
// and argv[] is the array of command line arguments, as character strings.
// I'll tell you more about the command line arguments below.
*/
int main( int argc, char * argv[] )
{
   /* All the variables here are declared as
   // double length floating point.
   // Accuracy is usually more than 11 useful digits,
   // Exponent range is usually more than about 10^(+-235).
   // Simplify way too much,
   // you can write numbers from 1e-235 to 1e235,
   // and, if we get the math write,
   // we can guarantee at least 11 good digits for pi.
   //
   // (Actually, the math normally supported in modern C libraries
   // is IEEE 754, which would be 10^(+-308) and at least 15 good digits.
   // Check C Language Data Types and IEEE 754 on Wikipedia, stackexchange,
   // etc., for more information.
   //
   // If you want to know what your compiler gives,
   // #include <limits.h> and play with the math.)
   */
   double area = 0.0; /* Sum the area here. */
   double deltaX = 0.025; /* Width of the rectangles (by default 1/40). */
   double lastX = -1.0; /* Track the left side. */
   double x; /* The right side. */
   double almostPi; /* The approximation of pi. */
   /* You will notice that we declare the names and types
   // of the variables before we use them.
   // C also allows us to set the initial value as we declare them.
   */

   if ( argc > 1 ) /* Command line arguments? */
   {  deltaX = strtod( argv[ 1 ], NULL ); /* Assume it's a different width. */
   }
   /* This allows us to set a different width on the command line.
   // 1/11 would be 0.09090909090909090909 to twenty decimal places:
   // ./halfpiArea 0.09090909090909090909
   // 1/8 == 0.125, 1/10 == 0.1, 1/100 == 0.01, 1/200000 == 0.000005
   */

   /* Note that starting from the half delta converges at least one decimal digit faster.
   // (That's because the height of the rectangle crosses the circle at midpoint,
   // rather than left edge or right. Draw the pictures to see why.
   // This is right edge, but changing to midpoint or left edge is trivial.)
   */
   for ( x = -1.0 + deltaX; x <= 1.0; x += deltaX )
   {
      double ysquared = 1.0 - ( x * x );
      double y = sqrt( ysquared );
      /* deltaX = x - lastX; Reminder for humans, only! */
      lastX = x;
      area += deltaX * y;
      /* Suppress long listings of the areas.*/
      if ( deltaX > 0.00005 ) /* 1/20000 */
      {  printf( "(%17.15g,%17.15g): %17.15g\n", x, y, area );
         /* %17.15g is a floating point field
         // of 17 digits with 15 digits to the right of the decimal.
         */
      }
   }

   almostPi = area * 2;
   printf( "Almost Pi: %17.15g\n", almostPi );
   return EXIT_SUCCESS;
}



Calculating π to eleven digits works okay with C doubles. But if you look at the listing of the areas, you see some odd differences, because C always rounds but bc always does as many exact digits as you tell it to with the scale variable.

Play with different widths, compare to the bc version, and see what you can learn.

(Modern C, from about C99, allows you to declare floating point types that use alternative rounding.)

And you should notice that C is very fast when compared to bc.

When you want to check the real value of π, remember:
echo "scale=41; a(1)*4;" | bc -l
from the shell command line, or
scale=41
a(1)*4
in bc, adjust scale to one more than the number of good digits you want.

And don't try 
./halfpiArea 0.0000000000000000000000000000000000000001
unless you really want to watch your computer contemplate its navel until you give up and shut it down. Start with
./halfpiArea 0.00001
and work your way up to 0.0000001 or so, where you get really noticeable waits.

And see if you can figure out whether midpoint is really better than right side, as I said it would be, when you start using widths smaller than 1/100 or so.

Oh. And play with the source code a bit, too.

[JMR201611182140:
I decided to post a few hints about playing with the programs:
http://joels-programming-fun.blogspot.com/2016/11/using-to-check-your-floating-point.html.  
]


[JMR201611292224:
Hopefully, I'll get a chance to post sample source with the GMP gnu multi-precision library "real soon now". (I've got one running, it just seems to take more time to write the rant than the source code.)
Here it is, demonstrating the principle with GMP:
http://joels-programming-fun.blogspot.com/2016/11/using-gmp-to-approximate-pi.html.
]

Saturday, January 9, 2016

Stemming the Tide: RC Time Delays with bc

This one was inspired by this post on debian-user:
https://lists.debian.org/debian-use/2015/12/msg00627.html
I mentioned in my general blog that I feel kind of sad that this kind of information is getting harder to find:
http://reiisi.blogspot.jp/2015/12/about-breaks-my-heart.html
Someday I'll expand on the idea of too much information being less information in my freedom isn't free blog blog

Today, I'm going to try to stem the tide a little bit, by talking about using tools that are should be available on everyone's computer or smart phone terminal to help solve a moderately simple electronics problem. 

One of the regular participants in the debian-user mailing list had a question about using an RC circuit. He has a DC powered milling machine that he needed to build a power supply for, and he wanted to build a switching power supply. 

Switching power supplies, without a slow-start circuit, draw enough power to throw your circuit breaker on start-up, so he needed to build a slow-start circuit. And he needed help with the physics and was asking for a tool to help do a units conversion.

The physics he needed are mostly explained on these pages on wikipedia:
https://en.wikipedia.org/wiki/RC_circuit
https://en.wikipedia.org/wiki/RC_time_constant
That helped me refresh my memory, as well.

He didn't need the conversion as much as the formula for the delay characteristics, and he also needed a calculator that would work the formula for him in a loop so he could try different sizes of capacitors and resistors and get an idea of the power draw over time.

There are lots of calculators that let you punch in numbers, but when you have to repeat the same series of calculations lots of times, you tend to make mistakes. That's one of the reasons being able to run a loop is important.

When you think of a calculation loop, you tend to think of programming languages. Perl, Python, Ruby, FORTH, etc. But programming is a scary topic for many people. (Most people? I think it should be taught in the primary grades, myself.) 

If you know about the interactive modes of the above four languages, you know that they can be used like a programmable calculator. 

Perl is installed as a part of almost every Unix-like OS. But perl's use of dollar signs makes it feel arcane, and if you have heard of regular expressions, you may think the name of the language makes your head implode. 

FORTH's postfix notation may also make you hesitate.

Python and Ruby may need to be installed, and installing may be scary.

Maybe all of that tends to make you shy away from programming.

But there is a basic, easy-to-use, multi-precision calculator, bc, that is available in just about every workstation or server installation of various Unix and Unix-like OSses, including Linux and (I think) still on Mac OS X (but not Android!), and can even be installed on MSWindows. (But not so easily on Android! No-root-debian is a bit of an SD hog.)

Okay, so, enough with the sales pitch for bc. Let's see it in action on this problem.

WARNING!

I demonstrate some idealized circuits below. They are dangerous as shown for a variety of reasons. Electronics parts used incorrectly can burn, explode, cause serious damage, and cause permanent injury including blindness, lung damage, and death.

If you don't know how to build circuits like these correctly, you are very likely to hurt yourself and others badly. If you know how to build these circuits correctly, you should have no reason to build them.

And any rate, don't try to build them without proper supervision by people who know what they are doing.

If you really want to experiment with this stuff, find yourself a switching power supply kit from a reputable kit seller and build that, instead.

Switching power supplies are kind of like the bucket showers you use at camp. Fill it up and you can get a nice shower for a short time. As long as you keep it more than half-full, the stream from the shower is, well, not too bad.

Unfortunately, the bucket metaphor gets really laborious from there. Wikipedia to the rescue again, if you want to know more:
https://en.wikipedia.org/wiki/Switched-mode_power_supply
In the metaphor, we use a capacitor like a bucket:


But if you hook a capacitor across line voltage this way in your house, the capacitor suddenly draws a very huge current for a few milliseconds, 120 times a second.

That's dangerous! 

A few milliseconds every eight and a third milliseconds is not a short time, really. We'll see that better, below.  

[Update 20160111 part 2:
Not incidentally, most large capacitors are polarized. A polarized capacitor hooked as above across an AC house power source will definitely burn, and very probably explode. That is, I don't know of a polarized capacitor in production with a high enough reverse rating to withstand household AC power.

(Polarized capacitors must be hooked up correctly, positive and negative to the corresponding power wires. AC power is switching positive and negative 60 times a second, so there is no way to hook it up correctly without rectifying it first.)
]

[Updated to correctly describe the AC physics. Apologies to the first ten or so people who read this rant while I was forgetting to explicitly deal with the current reversals inherent in AC. JMR20160111]

If the power were DC, that current would quickly drop to a sort-of manageable current as the capacitor charges. [Update JMR20160111: In AC, as above, in the milliseconds after the capacitor is charged a little, it gets forcibly discharged, which is also potentially dangerous.]

If the capacitor is very large at all, it could pop the circuit breaker, cause brown-outs and spikes, and do other things that are not nice to your power circuit and devices plugged in on it [Update JMR20160111: ... assuming the capacitor is properly rated and can survive being hooked up like this]

If your house is not well isolated electrically from your neighbors' houses, they would have serious reason to complain, too. 

So you want a resistor in series with the capacitor. And let's rectify the house current, while we're at it, so we can keep the capacitor charged.


Now, ultimately, we'll have some other circuitry, a relay, or an SCR or something, to take that resistor out (effectively short across it) once the capacitor is charged. Otherwise, the device is not going to get enough power to run, because the resistor will be eating up the power, instead.

So, you want to know how big a resistor you need for a capacitor large enough to drive the device. 

Say you don't want to draw more than 500 Watts instantaneously. 120 volts AC will push 170 volt peaks out of an ideal rectifier, so we'll use 170 volts in the formula. At start, the capacitor will be a dead short, so the resistor is what you pick. Remember Ohm's law:
  • E = IR and P = IE
  • R = E/I because we want R
  • I = P/E so we can get rid of I
  • subsitute: R = E/(P/E) 
  • normalize: R = E(E/P) or E2/P
  • substituting the numbers: R = 1702/500
You don't really need a calculator for this, do you? (Heh.)


Well, okay, you don't want to rely only on doing it in your head. Get a shell and fire up bc. You might as well bring in the math library, too:

$ bc -l
170*170/500
57.80000000000000000000
A sixty ohm, 500 watt resistor will keep the house from catching on fire, and be friendly to the circuit breaker and all your appliances and your neighbors.

Double checking,
$ 170/60
2.83333333333333333333
120/60
2.00000000000000000000
120*2
240
2.833 instantaneous amps. That's about what to expect, given the numbers, but, that's a lot of current. Let's see. 500 watts is a samll to medium room heater. Even preferring the RMS power, 240 watts is still a lot of heat if the capacitor should ever fail shorted.

Which wouldn't protect the house from burning down in the worst case, after all.

So, instead of a 240 watt resistor, we actually want a resistive fuse able to handle about 500 watts for a fraction of a second, but burning out if it sustains more than 250 watts (2 amps) for more than several seconds.

But the mill then needs to be able to safely power down suddenly if we do it that way. So, probably, we'll actually use a high-power transistor here that senses the voltage on the capacitor and drops the impedance as the capacitor charges. But that's for a later step. Right now we want to know limits.

We want an idea how fast the capacitor will charge. That's where the ability to run loops in bc comes in handy:

You can do this in bc:


define vc(vs,t,r,c) {
  return vs * (1-e(-t/(r*c)));
}

scale = 5;
vs = 120;
r = 60;
c = .01;
for ( t=0.0; t<5.0; t += 0.1 ) {  
  vc = vc(vs,t,r,c );
  vr = vs - vc;
  a = vr / r;
  p = vr * a;
  print "t: ", t, "  vc:", vc, "  a: ", a, "  p: ", p,  "\n"
}


[Update JMR200160111:

Select between the lines in your browser, and paste with right-click into the terminal bc session you opened up for the simple calculations above. ("bc -l" if you didn't do that yet.) 

You may need to hit the return key after pasting the code in. 

It works in the version of bc in Debian. openbsd's bc wants variable names to be only one letter long:

define v(s,t,r,c) {
  return s * (1-e(-t/(r*c)));
}

scale = 5;
s = 120;
r = 60;
c = .01;
for ( t=0.0; t<5.0; t += 0.1 ) {  
  v = v(s,t,r,c );
  z = s - v;
  a = z / r;
  p = z * a;
  print "t: ", t, "  vc:", v, "  a: ", a, "  p: ", p,  "\n"
}

]

And the output looks like this:

t: 0  vc:0  a: 2.00000  p: 240.00000
t: .1  vc:18.42240  a: 1.69296  p: 171.96681
t: .2  vc:34.01640  a: 1.43306  p: 123.21965
t: .3  vc:47.21640  a: 1.21306  p: 88.29087
t: .4  vc:58.38960  a: 1.02684  p: 63.26402
t: .5  vc:67.84920  a: .86918  p: 45.32843
t: .6  vc:75.85560  a: .73574  p: 32.47880
t: .7  vc:82.63200  a: .62280  p: 23.27279
t: .8  vc:88.36920  a: .52718  p: 16.67512
t: .9  vc:93.22440  a: .44626  p: 11.94887
t: 1.0  vc:97.33560  a: .37774  p: 8.56125
t: 1.1  vc:100.81440  a: .31976  p: 6.13478
t: 1.2  vc:103.76040  a: .27066  p: 4.39541
t: 1.3  vc:106.25400  a: .22910  p: 3.14920
t: 1.4  vc:108.36360  a: .19394  p: 2.25676
t: 1.5  vc:110.15040  a: .16416  p: 1.61691
t: 1.6  vc:111.66240  a: .13896  p: 1.15859
t: 1.7  vc:112.94280  a: .11762  p: .83006
t: 1.8  vc:114.02640  a: .09956  p: .59473
t: 1.9  vc:114.94320  a: .08428  p: .42618
t: 2.0  vc:115.71960  a: .07134  p: .30536
t: 2.1  vc:116.37720  a: .06038  p: .21874
t: 2.2  vc:116.93280  a: .05112  p: .15679
t: 2.3  vc:117.40440  a: .04326  p: .11228
t: 2.4  vc:117.80280  a: .03662  p: .08046
t: 2.5  vc:118.14000  a: .03100  p: .05766
t: 2.6  vc:118.42560  a: .02624  p: .04131
t: 2.7  vc:118.66800  a: .02220  p: .02957
t: 2.8  vc:118.87200  a: .01880  p: .02120
t: 2.9  vc:119.04600  a: .01590  p: .01516
t: 3.0  vc:119.19240  a: .01346  p: .01087
t: 3.1  vc:119.31600  a: .01140  p: .00779
t: 3.2  vc:119.42160  a: .00964  p: .00557
t: 3.3  vc:119.51040  a: .00816  p: .00399
t: 3.4  vc:119.58600  a: .00690  p: .00285
t: 3.5  vc:119.64960  a: .00584  p: .00204
t: 3.6  vc:119.70360  a: .00494  p: .00146
t: 3.7  vc:119.74920  a: .00418  p: .00104
t: 3.8  vc:119.78760  a: .00354  p: .00075
t: 3.9  vc:119.82000  a: .00300  p: .00054
t: 4.0  vc:119.84760  a: .00254  p: .00038
t: 4.1  vc:119.87160  a: .00214  p: .00027
t: 4.2  vc:119.89080  a: .00182  p: .00019
t: 4.3  vc:119.90760  a: .00154  p: .00014
t: 4.4  vc:119.92200  a: .00130  p: .00010
t: 4.5  vc:119.93400  a: .00110  p: .00007
t: 4.6  vc:119.94480  a: .00092  p: .00005
t: 4.7  vc:119.95320  a: .00078  p: .00003
t: 4.8  vc:119.96040  a: .00066  p: .00002
t: 4.9  vc:119.96640  a: .00056  p: .00001

Which tells you that, for a 10,000 microfarad capacitor, you'll get close to full power after two seconds, and that power has dropped below a quarter watt by 2.1 seconds.

Not counting the load of the device.

See how useful bc is?

[Update JMR20160111:

I left out a few things last Saturday night.

First, let's get a look at the charge curve for a straight wire (zero ohm resistor) in the rectified DC circuit. Remember that bare wire has a very small amount of resistance. We'll use 10 milli-ohms, and we'll focus on the first ten milliseconds.

Keep your bc session open, or start it again ("bc -l") and paste the above code in again. Then try this (as in the gnu bc version that works on debian):


r = 0.01;
for ( t=0.0; t<0.0101; t += 0.001 ) {  
  vc = vc(vs,t,r,c );
  vr = vs - vc;
  a = vr / r;
  p = vr * a;
  print "t: ", t, "  vc:", vc, "  a: ", a, "  p: ", p,  "\n"
}


Or, for the version of bc in openbsd:


r = 0.01;
for ( t=0.0; t<0.0101; t += 0.001 ) {  
  v = v(s,t,r,c );
  z = s - v;
  a = z / r;
  p = z * a;
  print "t: ", t, "  vc:", v, "  a: ", a, "  p: ", p,  "\n"
}



Here's what you should see:
t: 0  vc:0  a: 12000.00000  p: 1440000.00000
t: .001  vc:119.99520  a: .48000  p: .00230
t: .002  vc:120.00000  a: 0  p: 0
t: .003  vc:120.00000  a: 0  p: 0
t: .004  vc:120.00000  a: 0  p: 0
t: .005  vc:120.00000  a: 0  p: 0
t: .006  vc:120.00000  a: 0  p: 0
t: .007  vc:120.00000  a: 0  p: 0
t: .008  vc:120.00000  a: 0  p: 0
t: .009  vc:120.00000  a: 0  p: 0
t: .010  vc:120.00000  a: 0  p: 0
Wow!

12 kiloamps of instantaneous current!

1.44 megawatts of instantaneous power!

Fortunately, the resistance in the house wire will limit the current, so you wouldn't really draw that much. But it would try, and the circuit breaker would (hopefully) keep you from taking down the neighborhood grid. And you might be fumbling around in the circuit breaker box in the dark. Assuming you survive when the capacitor and rectifier probably explode and burn.

Now you have mathematical proof of why you don't want to build that circuit without the current limit resistor.

Do you wonder about the AC circuit? So do I, but I have a day job, so I'll save that for another rant. But it's not that hard. You just have to fix the loop so that it simulates the source voltage doing a sine curve from -170 to 170 peak-to-peak.

A few comments about real power supplies in recent electronic (especially after about the year 1997 or so):

The switch that removes the current-limiting resistor should be driven by a schottky diode circuit or some similar circuit that monitors the voltage across the capacitor, removing the current limit as the capacitor gains sufficient charge to drive the device at its rated voltage. Maybe I'll draw a model circuit for that when I have more time.

But, truth be told, we don't do it this way any more. We use a small microcontroller, itself powered by a very low power switching power supply, to control some sort of high-powered semiconductor switch (SCR or something similar) to act as the current-limiting resistor in the above circuit. Or we may use a  The microcontroller monitors the current draw with analog-to-digital inputs, for safety and soft startup.

It also monitors the voltage across the impedance device (SCR or whatever) directly, ramping the current-limiting impedance down smoothly as the power capacitor charges and the voltage across the impedance decreases.

I don't think I'm going to have time very soon to work up an example microcontroller-controlled switching power supply circuit, and the control logic for it, that which wouldn't be covered by current patents. (Stupid patents.)

End-Update]