Canola  0.8.D001
lib/number/sqrt.cc
Go to the documentation of this file.
00001 //
00002 // canola - canon canola 1614p emulator
00003 // Copyright (C) 2011, 2012 Peter Miller
00004 //
00005 // This program is free software; you can redistribute it and/or modify
00006 // it under the terms of the GNU General Public License, version 3, as
00007 // published by the Free Software Foundation.
00008 //
00009 // This program is distributed in the hope that it will be useful,
00010 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012 // General Public License for more details.
00013 //
00014 // You should have received a copy of the GNU General Public License along
00015 // with this program. If not, see <http://www.gnu.org/licenses/>.
00016 //
00017 
00018 #include <lib/ac/assert.h>
00019 
00020 #include <lib/number.h>
00021 
00022 
00023 number
00024 number::sqrt(void)
00025     const
00026 {
00027     if (is_zero())
00028         return number();
00029 
00030     enum { blue_shift = 15 };
00031     assert(blue_shift < 16);
00032 
00033     //
00034     // Instruction Manual, p. 8
00035     //
00036     // "Square Root Key
00037     //
00038     // "Used for finding square roots.  When this key is depressed the
00039     // indicated value is square rooted.  The result of the square root
00040     // operation is indicated by a positive value regardless of positive or
00041     // negative value.  It is not necessary to depress the [+=] key after
00042     // this key has been depressed."
00043     //
00044     number_z a(value.shift_left(2 * blue_shift - decimal));
00045 
00046     //
00047     // We are going to use a Newton-Raphson approximation
00048     // with quadratic convergence.
00049     //
00050     // FIXME: How about Halley's Method, with cubic convergence?
00051     //        The 1614P almost certainly did N-R in hardware.
00052     //
00053     number_z epsilon(1000u);
00054     number_z x = number_z::one().shift_left(blue_shift + decimal / 2);
00055     for (;;)
00056     {
00057         number_z x2 = (x + a / x) / 2u;
00058         if ((x2 < x ? x - x2 : x2 - x) < epsilon)
00059             break;
00060         x = x2;
00061     }
00062 
00063     //
00064     // Because it is a square root, the number of digits to the left of
00065     // the decimal point always reduces or stays the same.
00066     // This means we will never get an overflow.
00067     //
00068     assert(x < number_z::one().shift_left(16 + blue_shift));
00069 
00070     unsigned x_dec = blue_shift;
00071     truncate_floating_operation(x, x_dec);
00072 
00073     // <question>
00074     // Does sqrt strip redundant zero decimal places to the right of the
00075     // decimal point?  Does [9] [sqrt] display "3." or "3.0000000000000000"?
00076     // </question>
00077     strip_redundant_decimal_places(x, x_dec);
00078 
00079     return number(x, x_dec);
00080 }
00081 
00082 
00083 // vim: set ts=8 sw=4 et :