singe/io/numstr.cpp
2019-11-11 14:53:02 -06:00

288 lines
7.6 KiB
C++

/*
* mpo_numstr.cpp
*
* Copyright (C) 2005 Matthew P. Ownby
*
* This file is part of MPOLIB, a multi-purpose library
*
* MPOLIB is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* MPOLIB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
// MPO's NOTE:
// I may wish to use MPOLIB in a proprietary product some day. Therefore,
// the only way I can accept other people's changes to my code is if they
// give me full ownership of those changes.
#include "numstr.h"
#ifndef WIN32
#include <ctype.h> // for toupper
#endif
const char *DIGITS = "0123456789ABCDEF";
// NOTE : this function doesn't do full safety checking in the interest of simplicity and speed
int numstr::ToInt32(const char *str)
{
const int BASE = 10; // for now we always assume base is 10 because I never seem to use anything else
int result = 0;
bool found_first_digit = false; // whether we have found the first digit or not
int sign_mult = 1; // 1 if the number is positive, -1 if it's negative
for (unsigned int i = 0; i < my_strlen(str); i++)
{
if (!found_first_digit)
{
if (is_digit(str[i], BASE))
{
found_first_digit = true;
}
// if it a negative number?
else if (str[i] == '-')
{
sign_mult = -1;
}
// else it's an unknown character, so we ignore it until we get to the first digit
}
// note: we do not want this to be an "else if" because the above 'if' needs to flow into this
if (found_first_digit)
{
// make sure we aren't dealing with any non-integers
if ((str[i] >= '0') && (str[i] <= '9'))
{
result *= BASE;
result += str[i] - '0';
}
// else we've hit unknown characters so we're done
else
{
break;
}
}
}
return result * sign_mult;
}
unsigned int numstr::ToUint32(const char *str, int base)
{
unsigned int u = 0;
ToUint(str,u,base);
return u;
}
MPO_UINT64 numstr::ToUint64(const char *str, int base)
{
MPO_UINT64 u = 0;
ToUint(str,u,base);
return u;
}
double numstr::ToDouble(const char *str)
{
const double BASE = 10.0; // this makes it easier for now ...
const double BASE_DIVIDE = 0.1; // because multiplaying by 0.1 is faster than dividing by 10.0
bool found_period = false; // whether we've encountered the period in the double yet
bool found_first_digit = false;
double result = 0.0;
double divide_by = 1.0; // after we pass the decimal point, we need to divide subsequent numbers to put them in their proper sphere
double sign_mult = 1.0; // final result is multiplied by this to set the sign
for (unsigned int i = 0; i < my_strlen(str); i++)
{
if (!found_first_digit)
{
if (is_digit(str[i], (int) BASE))
{
found_first_digit = true;
}
// if it a negative number?
else if (str[i] == '-')
{
sign_mult = -1.0;
}
else if (str[i] == '.')
{
found_period = true;
}
// else it's an unknown character, so we ignore it until we get to the first digit
}
// note: we do not want this to be an "else if" because the above 'if' needs to flow into this
if (found_first_digit)
{
// make sure we aren't dealing with any non-integers
if ((str[i] >= '0') && (str[i] <= '9'))
{
// if we haven't encountered the decimal place yet
if (found_period == false)
{
result *= BASE;
result += str[i] - '0';
}
// if we are passed the decimal place
else
{
divide_by *= BASE_DIVIDE; // each new digit we find needs to be divided by 10x the previous divide_by value
result += ((str[i] - '0') * divide_by);
}
}
// else if we've hit the decimal point
else if (str[i] == '.')
{
found_period = true;
}
// else we've hit unknown characters so we're done
else
{
break;
}
}
}
return result * sign_mult;
}
// DUMMY functions to force template instantiation
// (since I can't figure out how to make the compiler do this without! arrgh)
string numstr::ToStr(int num, int base, unsigned int min_digits)
{
unsigned int dummy = 0; // see comment in .h for IToStr for explanation
return IToStr(num, dummy, base, min_digits);
}
string numstr::ToStr(MPO_INT64 num, int base, unsigned int min_digits)
{
MPO_UINT64 dummy = 0; // see comment in .h for IToStr for explanation
return IToStr(num, dummy, base, min_digits);
}
string numstr::ToStr(unsigned char c, int base, unsigned int min_digits)
{
return UToStr(c, base, min_digits);
}
string numstr::ToStr(unsigned int u, int base, unsigned int min_digits)
{
return UToStr(u, base, min_digits);
}
string numstr::ToStr(MPO_UINT64 u, int base, unsigned int min_digits)
{
return UToStr(u, base, min_digits);
}
string numstr::ToStr(double d, unsigned int min_digits_before, unsigned int min_digits_after, unsigned int max_digits_after)
{
string result = "(overflow)";
const double BASE = 10.0; // we will only support base 10 with doubles to simplify things ...
unsigned int decimal_length = 0;
// bounds check: make sure the double is within our limits (2^62 was the highest I could get it to work without introducing overflow errors)
if ((d <= 4611686018427387904.0) && (d >= -4611686018427387904.0))
{
MPO_INT64 int64_portion = (MPO_INT64) d; // strip off floating point part
d = d - int64_portion; // isolate just the decimal portion
result = ToStr(int64_portion, 10, min_digits_before); // use our other function to calculate the int portion
result = result + "."; // add decimal place, it will always be displayed even if there is no fractional value to this number
if (d < 0) d *= -1.0; // force d to be positive
// NOTE : d will now always be positive
do
{
d *= BASE; // move decimal point one notch to the right
int int_portion = (int) d; // grab the number that is above the decimal point
result = result + DIGITS[int_portion];
d = d - int_portion;
decimal_length++; // gotta keep track of this for 'min_digits_after' calculation
} while ((d != 0.0) && (max_digits_after > decimal_length));
while (decimal_length < min_digits_after)
{
result = result + "0"; // pad trailing zeroes
decimal_length++;
}
}
// else return default result to indicate error
return result;
}
string numstr::ToUnitStr(MPO_UINT64 u)
{
string result;
double d;
// if less than 1 k
if (u < 1024)
{
result = ToStr(u) + " B";
}
// less than 1 meg
else if (u < 1048576)
{
d = u * 0.0009765625; // same as dividing by 1024
result = ToStr(d, 0, 1, 2) + " KiB";
}
// less than 1 gig
else if (u < 1073741824)
{
d = u / (1048576.0); // convert to megs
result = ToStr(d, 0, 1, 2) + " MiB";
}
// else leave it as gigs, we won't go any higher for now
else
{
d = u / (1073741824.0); // convert to gigs
result = ToStr(d, 0, 1, 2) + " GiB";
}
return result;
}
unsigned int numstr::my_strlen(const char *s)
{
unsigned int i = 0;
while (s[i] != 0) i++;
return i;
}
////////////////////////////////
// private funcs
inline bool numstr::is_digit(char ch, int base)
{
if ((base == 10) && (ch >= '0') && (ch <= '9')) return(true);
else if ((base == 16) && (
((ch >= '0') && (ch <= '9')) ||
((toupper(ch) >= 'A') && (toupper(ch) <= 'F'))
)) return(true);
// else no other base is supported
return false;
}