Canola  0.8.D001
lib/number/round.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 #include <lib/ac/math.h>
00020 
00021 #include <lib/number.h>
00022 
00023 
00024 number
00025 number::round_up(int preferred_decimal)
00026     const
00027 {
00028     if (has_overflowed())
00029         return *this;
00030 
00031     // Strictly speaking, this rounds away from zero; it does not round
00032     // towards positive infinity, like ANSI C ceil(3) does.
00033     assert(preferred_decimal >= 0);
00034     assert(preferred_decimal < 16);
00035     if (decimal == (unsigned)preferred_decimal)
00036         return *this;
00037 
00038     if (decimal < (unsigned)preferred_decimal)
00039     {
00040         size_t more = preferred_decimal - decimal;
00041         return number(value.shift_left(more), preferred_decimal, negative);
00042     }
00043 
00044     number_z n(value);
00045     size_t dump = decimal - preferred_decimal;
00046     assert(dump > 0);
00047 
00048     // <question>
00049     // When rounding UP to zero decimals, does 2.01 become 2 or 3?  This
00050     // is a question of does it only consult the first decimal, or all
00051     // the decimals to be round up?  (One is fast, the other is accurate.)
00052     // </question>
00053 #if 1
00054     bool nudge = (n % (number_z::one().shift_left(dump))).is_not_zero();
00055 #else
00056     bool nudge = (n.get(dump - 1) != 0);
00057 #endif
00058 
00059     n = n.shift_right(dump);
00060     if (nudge)
00061         n += number_z::one();
00062 
00063     return number(n, preferred_decimal, negative);
00064 }
00065 
00066 
00067 number
00068 number::round_off(int preferred_decimal)
00069     const
00070 {
00071     if (has_overflowed())
00072         return *this;
00073 
00074     assert(preferred_decimal >= 0);
00075     assert(preferred_decimal < 16);
00076     if (decimal == (unsigned)preferred_decimal)
00077         return *this;
00078 
00079     if (decimal < (unsigned)preferred_decimal)
00080     {
00081         size_t more = preferred_decimal - decimal;
00082         return number(value.shift_left(more), preferred_decimal, negative);
00083     }
00084 
00085     number_z n(value);
00086     size_t dump = decimal - preferred_decimal;
00087     assert(dump > 0);
00088 
00089     bool nudge = (n.get(dump - 1) >= 5);
00090 
00091     n = n.shift_right(dump);
00092     if (nudge)
00093         n += number_z::one();
00094 
00095     return number(n, preferred_decimal, negative);
00096 }
00097 
00098 
00099 number
00100 number::round_down(int preferred_decimal)
00101     const
00102 {
00103     if (has_overflowed())
00104         return *this;
00105 
00106     // Strictly speaking, this rounds towards zero; it does not round
00107     // towards negative infinity, like ANSI C floor(3) does.
00108     assert(preferred_decimal >= 0);
00109     assert(preferred_decimal < 16);
00110     if (decimal == (unsigned)preferred_decimal)
00111         return *this;
00112 
00113     if (decimal < (unsigned)preferred_decimal)
00114     {
00115         size_t more = preferred_decimal - decimal;
00116         return number(value.shift_left(more), preferred_decimal, negative);
00117     }
00118 
00119     size_t dump = decimal - preferred_decimal;
00120     assert(dump > 0);
00121 
00122     return number(value.shift_right(dump), preferred_decimal, negative);
00123 }
00124 
00125 
00126 // vim: set ts=8 sw=4 et :