added: struct: Conv

consists of some parameters used
         in ToString() and FromString()
added:   Big::ToString() can group digits
         e.g. 1234567 -> 1`234`567
added:   Parser::SetGroup(int g)
         Parser::SetComma(int c, int c2 = 0)
         Parser::SetParamSep(int s)
added:   uint Big::ToString(std::string & result, const Conv & conv)
         uint Big::ToString(std::wstring & result, const Conv & conv)
         std::string Big::ToString(const Conv & conv) const
         std::string Big::ToString()
added:   uint FromString(const char * source, const Conv & conv, const char **, bool *)
         uint FromString(const wchar_t * source, const Conv & conv, const wchar_t **, bool *)
         uint FromString(const std::string & string, const Conv & conv, const wchar_t **, bool *)
         uint FromString(const std::wstring & string, const Conv & conv, const wchar_t **, bool *)
removed: macros: TTMATH_COMMA_CHARACTER_1 and TTMATH_COMMA_CHARACTER_2
         the comma characters we have in Conv struct now



git-svn-id: svn://ttmath.org/publicrep/ttmath/trunk@226 e52654a7-88a9-db11-a3e9-0013d4bc506e
This commit is contained in:
Tomasz Sowa 2009-11-01 01:40:40 +00:00
parent 413c83de45
commit 2feabc64e2
4 changed files with 566 additions and 256 deletions

View File

@ -1,4 +1,4 @@
Version 0.9.0 prerelease (2009.10.25): Version 0.9.0 prerelease (2009.11.01):
* added: support for wide characters (wchar_t, std::wstring) * added: support for wide characters (wchar_t, std::wstring)
* added: Big::IsInteger() * added: Big::IsInteger()
returns true if the value is integer (without fraction) returns true if the value is integer (without fraction)
@ -28,6 +28,22 @@ Version 0.9.0 prerelease (2009.10.25):
(at least one mathematical operator was used or a function or variable) (at least one mathematical operator was used or a function or variable)
* added: to the parser: operator percentage * added: to the parser: operator percentage
e.g. 1000-50%=1000-(1000*0,5)=500 e.g. 1000-50%=1000-(1000*0,5)=500
* added: struct: Conv
consists of some parameters used
in ToString() and FromString()
* added: Big::ToString() can group digits
e.g. 1234567 -> 1`234`567
* added: Parser::SetGroup(int g)
Parser::SetComma(int c, int c2 = 0)
Parser::SetParamSep(int s)
* added: uint Big::ToString(std::string & result, const Conv & conv)
uint Big::ToString(std::wstring & result, const Conv & conv)
std::string Big::ToString(const Conv & conv) const
std::string Big::ToString()
* added: uint FromString(const char * source, const Conv & conv, const char **, bool *)
uint FromString(const wchar_t * source, const Conv & conv, const wchar_t **, bool *)
uint FromString(const std::string & string, const Conv & conv, const wchar_t **, bool *)
uint FromString(const std::wstring & string, const Conv & conv, const wchar_t **, bool *)
* changed: Factorial() is using the Gamma() function now * changed: Factorial() is using the Gamma() function now
* changed: Big::Div(ss2) * changed: Big::Div(ss2)
Big::Mod(ss2) Big::Mod(ss2)
@ -36,7 +52,9 @@ Version 0.9.0 prerelease (2009.10.25):
* removed: Parser<>::SetFactorialMax() method * removed: Parser<>::SetFactorialMax() method
the factorial() is such a fast now that we don't need the method longer the factorial() is such a fast now that we don't need the method longer
* removed: ErrorCode::err_too_big_factorial * removed: ErrorCode::err_too_big_factorial
* removed: macros: TTMATH_COMMA_CHARACTER_1 and TTMATH_COMMA_CHARACTER_2
the comma characters we have in Conv struct now
Version 0.8.6 (2009.10.25): Version 0.8.6 (2009.10.25):
* fixed: UInt::SetBitInWord(uint & value, uint bit) set 1 if the bit was * fixed: UInt::SetBitInWord(uint & value, uint bit) set 1 if the bit was

View File

@ -2809,82 +2809,114 @@ public:
/*! /*!
a method for converting the value into a string with a base equal 'base' a method for converting into a string
struct Conv is defined in ttmathtypes.h, look there for more information about parameters
input:
* base - a base (radix) on which the value will be shown
* if 'always_scientific' is true the result will be shown in 'scientific' mode
if 'always_scientific' is false the result will be shown
either as 'scientific' or 'normal' mode, it depends whether the abs(exponent)
is greater than 'when_scientific' or not, if it's greater the value
will be printed as 'scientific'
* 'max_digit_after_comma' - rounding
if 'max_digit_after_comma' is equal -1 that all digits in the mantissa
will be printed
if 'max_digit_after_comma' is equal or greater than zero
that only 'max_digit_after_comma' after the comma operator will be shown
(if 'max_digit_after_comma' is equal zero there'll be shown only
integer value without the comma)
for example when the value is:
12.345678 and max_digit_after_comma is 4
then the result will be
12.3457 (the last digit was rounded)
if there isn't the comma operator (when the value is too big and we're printing
it not as scientific) 'max_digit_after_comma' will be ignored
* if 'remove_trailing_zeroes' is true that not mattered digits
in the mantissa will be cut off
(zero characters at the end -- after the comma operator)
* decimal_point - a character which will be used as a decimal point
output: output:
return value: return value:
0 - ok and 'result' will be an object of type std::string (or std::wstring) which holds the value 0 - ok and 'result' will be an object of type std::string (or std::wstring) which holds the value
1 - if there was a carry (shoudn't be in a normal situation - if is that means there 1 - if there is a carry (it shoudn't be in a normal situation - if it is that means there
is somewhere an error in the library) is somewhere an error in the library)
*/ */
uint ToString( std::string & result, uint ToString( std::string & result,
uint base = 10, uint base = 10,
bool always_scientific = false, bool scient = false,
sint when_scientific = 15, sint scient_from = 15,
sint max_digit_after_comma = -1, sint comma_digits = -1,
bool remove_trailing_zeroes = true, bool trim_zeroes = true,
char decimal_point = TTMATH_COMMA_CHARACTER_1 ) const wchar_t comma = '.' ) const
{ {
return ToStringBase(result, base, always_scientific, when_scientific, Conv conv;
max_digit_after_comma, remove_trailing_zeroes, decimal_point);
conv.base = base;
conv.scient = scient;
conv.scient_from = scient_from;
conv.comma_digits = comma_digits;
conv.trim_zeroes = trim_zeroes;
conv.comma = static_cast<uint>(comma);
return ToStringBase<std::string, char>(result, conv);
} }
/*! /*!
a method for converting the value into a string with a base equal 'base' a method for converting into a string
struct Conv is defined in ttmathtypes.h, look there for more information about parameters
*/ */
uint ToString( std::wstring & result, uint ToString( std::wstring & result,
uint base = 10, uint base = 10,
bool always_scientific = false, bool scient = false,
sint when_scientific = 15, sint scient_from = 15,
sint max_digit_after_comma = -1, sint comma_digits = -1,
bool remove_trailing_zeroes = true, bool trim_zeroes = true,
wchar_t decimal_point = TTMATH_COMMA_CHARACTER_1 ) const wchar_t comma = '.' ) const
{ {
return ToStringBase(result, base, always_scientific, when_scientific, Conv conv;
max_digit_after_comma, remove_trailing_zeroes, decimal_point);
conv.base = base;
conv.scient = scient;
conv.scient_from = scient_from;
conv.comma_digits = comma_digits;
conv.trim_zeroes = trim_zeroes;
conv.comma = static_cast<uint>(comma);
return ToStringBase<std::wstring, wchar_t>(result, conv);
} }
/*!
a method for converting into a string
struct Conv is defined in ttmathtypes.h, look there for more information about parameters
*/
uint ToString(std::string & result, const Conv & conv) const
{
return ToStringBase<std::string, char>(result, conv);
}
/*!
a method for converting into a string
struct Conv is defined in ttmathtypes.h, look there for more information about parameters
*/
uint ToString(std::wstring & result, const Conv & conv) const
{
return ToStringBase<std::wstring, wchar_t>(result, conv);
}
/*!
a method for converting into a string
struct Conv is defined in ttmathtypes.h, look there for more information about parameters
*/
std::string ToString(const Conv & conv) const
{
std::string result;
ToStringBase<std::string, char>(result, conv);
return result;
}
/*!
a method for converting into a string
struct Conv is defined in ttmathtypes.h, look there for more information about parameters
*/
std::string ToString() const
{
Conv conv;
return ToString(conv);
}
private: private:
/*!
an auxiliary method for converting into the string
*/
template<class string_type, class char_type> template<class string_type, class char_type>
uint ToStringBase( string_type & result, uint ToStringBase(string_type & result, const Conv & conv) const
uint base,
bool always_scientific,
sint when_scientific,
sint max_digit_after_comma,
bool remove_trailing_zeroes,
char_type decimal_point) const
{ {
static char error_overflow_msg[] = "overflow"; static char error_overflow_msg[] = "overflow";
static char error_nan_msg[] = "NaN"; static char error_nan_msg[] = "NaN";
@ -2896,7 +2928,7 @@ private:
return 0; return 0;
} }
if( base<2 || base>16 ) if( conv.base<2 || conv.base>16 )
{ {
Misc::AssignString(result, error_overflow_msg); Misc::AssignString(result, error_overflow_msg);
return 1; return 1;
@ -2926,7 +2958,7 @@ private:
*/ */
Int<exp+1> new_exp; Int<exp+1> new_exp;
if( ToString_CreateNewMantissaAndExponent(result, base, new_exp) ) if( ToString_CreateNewMantissaAndExponent(result, conv, new_exp) )
{ {
Misc::AssignString(result, error_overflow_msg); Misc::AssignString(result, error_overflow_msg);
return 1; return 1;
@ -2936,16 +2968,14 @@ private:
we're rounding the mantissa only if the base is different from 2,4,8 or 16 we're rounding the mantissa only if the base is different from 2,4,8 or 16
(this formula means that the number of bits in the base is greater than one) (this formula means that the number of bits in the base is greater than one)
*/ */
if( base!=2 && base!=4 && base!=8 && base!=16 ) if( conv.base!=2 && conv.base!=4 && conv.base!=8 && conv.base!=16 )
if( ToString_RoundMantissa(result, base, new_exp, decimal_point) ) if( ToString_RoundMantissa<string_type, char_type>(result, conv, new_exp) )
{ {
Misc::AssignString(result, error_overflow_msg); Misc::AssignString(result, error_overflow_msg);
return 1; return 1;
} }
if( ToString_SetCommaAndExponent( result, base, new_exp, always_scientific, if( ToString_SetCommaAndExponent<string_type, char_type>(result, conv, new_exp) )
when_scientific, max_digit_after_comma,
remove_trailing_zeroes, decimal_point ) )
{ {
Misc::AssignString(result, error_overflow_msg); Misc::AssignString(result, error_overflow_msg);
return 1; return 1;
@ -2959,8 +2989,6 @@ private:
} }
private:
/*! /*!
in the method 'ToString_CreateNewMantissaAndExponent()' we're using in the method 'ToString_CreateNewMantissaAndExponent()' we're using
@ -2971,7 +2999,7 @@ private:
/*! /*!
an auxiliary method for converting into the string (private) an auxiliary method for converting into the string
input: input:
base - the base in range <2,16> base - the base in range <2,16>
@ -3030,16 +3058,16 @@ private:
new_exp = [log base (2^exponent)] + 1 <- where [x] means integer value from x new_exp = [log base (2^exponent)] + 1 <- where [x] means integer value from x
*/ */
template<class string_type> template<class string_type>
uint ToString_CreateNewMantissaAndExponent( string_type & new_man, uint base, uint ToString_CreateNewMantissaAndExponent( string_type & new_man, const Conv & conv,
Int<exp+1> & new_exp) const Int<exp+1> & new_exp) const
{ {
uint c = 0; uint c = 0;
if(base<2 || base>16) if( conv.base<2 || conv.base>16 )
return 1; return 1;
// the speciality for base equal 2 // the speciality for base equal 2
if( base == 2 ) if( conv.base == 2 )
return ToString_CreateNewMantissaAndExponent_Base2(new_man, new_exp); return ToString_CreateNewMantissaAndExponent_Base2(new_man, new_exp);
// this = mantissa * 2^exponent // this = mantissa * 2^exponent
@ -3054,7 +3082,7 @@ private:
// new_exp_ = [log base (2^exponent)] + 1 // new_exp_ = [log base (2^exponent)] + 1
Big<exp+1,man> new_exp_; Big<exp+1,man> new_exp_;
c += new_exp_.ToString_Log(temp, base); // this logarithm isn't very complicated c += new_exp_.ToString_Log(temp, conv.base); // this logarithm isn't very complicated
new_exp_.SkipFraction(); new_exp_.SkipFraction();
temp.SetOne(); temp.SetOne();
c += new_exp_.Add( temp ); c += new_exp_.Add( temp );
@ -3067,7 +3095,7 @@ private:
c += new_exp_.ToInt(new_exp); c += new_exp_.ToInt(new_exp);
// base_ = base // base_ = base
Big<exp+1,man> base_(base); Big<exp+1,man> base_(conv.base);
// base_ = base_ ^ new_exp_ // base_ = base_ ^ new_exp_
c += base_.Pow( new_exp_ ); c += base_.Pow( new_exp_ );
@ -3089,7 +3117,7 @@ private:
// (temp.Div( base_ )) the value of exponent should be equal zero or // (temp.Div( base_ )) the value of exponent should be equal zero or
// minimum smaller than zero then we've got the mantissa which has // minimum smaller than zero then we've got the mantissa which has
// maximum valid bits // maximum valid bits
temp.mantissa.ToString(new_man, base); temp.mantissa.ToString(new_man, conv.base);
// because we had used a bigger type for calculating I think we // because we had used a bigger type for calculating I think we
// shouldn't have had a carry // shouldn't have had a carry
@ -3307,7 +3335,7 @@ private:
(it's used in systems where the base is different from 2) (it's used in systems where the base is different from 2)
*/ */
template<class string_type, class char_type> template<class string_type, class char_type>
uint ToString_RoundMantissa(string_type & new_man, uint base, Int<exp+1> & new_exp, char_type decimal_point) const uint ToString_RoundMantissa(string_type & new_man, const Conv & conv, Int<exp+1> & new_exp) const
{ {
// we must have minimum two characters // we must have minimum two characters
if( new_man.length() < 2 ) if( new_man.length() < 2 )
@ -3322,8 +3350,8 @@ private:
// if the last character is greater or equal 'base/2' // if the last character is greater or equal 'base/2'
// we'll add one into the new mantissa // we'll add one into the new mantissa
if( digit >= base / 2 ) if( digit >= conv.base / 2 )
ToString_RoundMantissa_AddOneIntoMantissa(new_man, base, decimal_point); ToString_RoundMantissa_AddOneIntoMantissa<string_type, char_type>(new_man, conv);
return carry; return carry;
} }
@ -3335,7 +3363,7 @@ private:
this method addes one into the new mantissa this method addes one into the new mantissa
*/ */
template<class string_type, class char_type> template<class string_type, class char_type>
void ToString_RoundMantissa_AddOneIntoMantissa(string_type & new_man, uint base, char_type decimal_point) const void ToString_RoundMantissa_AddOneIntoMantissa(string_type & new_man, const Conv & conv) const
{ {
if( new_man.empty() ) if( new_man.empty() )
return; return;
@ -3348,13 +3376,13 @@ private:
// we can have the comma as well because // we can have the comma as well because
// we're using this method later in ToString_CorrectDigitsAfterComma_Round() // we're using this method later in ToString_CorrectDigitsAfterComma_Round()
// (we're only ignoring it) // (we're only ignoring it)
if( new_man[i] == decimal_point ) if( new_man[i] == static_cast<char_type>(conv.comma) )
continue; continue;
// we're adding one // we're adding one
uint digit = Misc::CharToDigit( new_man[i] ) + 1; uint digit = Misc::CharToDigit( new_man[i] ) + 1;
if( digit == base ) if( digit == conv.base )
digit = 0; digit = 0;
else else
was_carry = false; was_carry = false;
@ -3374,13 +3402,7 @@ private:
into the string into the string
*/ */
template<class string_type, class char_type> template<class string_type, class char_type>
uint ToString_SetCommaAndExponent( string_type & new_man, uint base, uint ToString_SetCommaAndExponent(string_type & new_man, const Conv & conv, Int<exp+1> & new_exp) const
Int<exp+1> & new_exp,
bool always_scientific,
sint when_scientific,
sint max_digit_after_comma,
bool remove_trailing_zeroes,
char_type decimal_point) const
{ {
uint carry = 0; uint carry = 0;
@ -3399,18 +3421,19 @@ private:
// there shouldn't have been a carry because we're using // there shouldn't have been a carry because we're using
// a greater type -- 'Int<exp+1>' instead of 'Int<exp>' // a greater type -- 'Int<exp+1>' instead of 'Int<exp>'
if( !always_scientific ) bool print_scientific = conv.scient;
if( !print_scientific )
{ {
if( scientific_exp > when_scientific || scientific_exp < -sint(when_scientific) ) if( scientific_exp > conv.scient_from || scientific_exp < -sint(conv.scient_from) )
always_scientific = true; print_scientific = true;
} }
// 'always_scientific' could be changed if( !print_scientific )
if( !always_scientific ) ToString_SetCommaAndExponent_Normal<string_type, char_type>(new_man, conv, new_exp);
ToString_SetCommaAndExponent_Normal(new_man, base, new_exp, max_digit_after_comma, remove_trailing_zeroes, decimal_point);
else else
// we're passing the 'scientific_exp' instead of 'new_exp' here // we're passing the 'scientific_exp' instead of 'new_exp' here
ToString_SetCommaAndExponent_Scientific(new_man, base, scientific_exp, max_digit_after_comma, remove_trailing_zeroes, decimal_point); ToString_SetCommaAndExponent_Scientific<string_type, char_type>(new_man, conv, scientific_exp);
return (carry==0)? 0 : 1; return (carry==0)? 0 : 1;
} }
@ -3420,25 +3443,22 @@ private:
an auxiliary method for converting into the string an auxiliary method for converting into the string
*/ */
template<class string_type, class char_type> template<class string_type, class char_type>
void ToString_SetCommaAndExponent_Normal( void ToString_SetCommaAndExponent_Normal(string_type & new_man, const Conv & conv, Int<exp+1> & new_exp ) const
string_type & new_man,
uint base,
Int<exp+1> & new_exp,
sint max_digit_after_comma,
bool remove_trailing_zeroes,
char_type decimal_point) const
{ {
if( !new_exp.IsSign() ) //if( new_exp >= 0 ) if( !new_exp.IsSign() ) // it means: if( new_exp >= 0 )
return ToString_SetCommaAndExponent_Normal_AddingZero(new_man, new_exp); ToString_SetCommaAndExponent_Normal_AddingZero<string_type, char_type>(new_man, new_exp);
else else
return ToString_SetCommaAndExponent_Normal_SetCommaInside(new_man, base, new_exp, max_digit_after_comma, remove_trailing_zeroes, decimal_point); ToString_SetCommaAndExponent_Normal_SetCommaInside<string_type, char_type>(new_man, conv, new_exp);
ToString_Group_man<string_type, char_type>(new_man, conv);
} }
/*! /*!
an auxiliary method for converting into the string an auxiliary method for converting into the string
*/ */
template<class string_type> template<class string_type, class char_type>
void ToString_SetCommaAndExponent_Normal_AddingZero(string_type & new_man, void ToString_SetCommaAndExponent_Normal_AddingZero(string_type & new_man,
Int<exp+1> & new_exp) const Int<exp+1> & new_exp) const
{ {
@ -3460,12 +3480,9 @@ private:
*/ */
template<class string_type, class char_type> template<class string_type, class char_type>
void ToString_SetCommaAndExponent_Normal_SetCommaInside( void ToString_SetCommaAndExponent_Normal_SetCommaInside(
string_type & new_man, string_type & new_man,
uint base, const Conv & conv,
Int<exp+1> & new_exp, Int<exp+1> & new_exp ) const
sint max_digit_after_comma,
bool remove_trailing_zeroes,
char_type decimal_point) const
{ {
// new_exp is < 0 // new_exp is < 0
@ -3477,7 +3494,7 @@ private:
// we're setting the comma within the mantissa // we're setting the comma within the mantissa
sint index = new_man_len - e; sint index = new_man_len - e;
new_man.insert( new_man.begin() + index, decimal_point); new_man.insert( new_man.begin() + index, static_cast<char_type>(conv.comma));
} }
else else
{ {
@ -3486,11 +3503,11 @@ private:
uint how_many = e - new_man_len; uint how_many = e - new_man_len;
string_type man_temp(how_many+1, '0'); string_type man_temp(how_many+1, '0');
man_temp.insert( man_temp.begin()+1, decimal_point); man_temp.insert( man_temp.begin()+1, static_cast<char_type>(conv.comma));
new_man.insert(0, man_temp); new_man.insert(0, man_temp);
} }
ToString_CorrectDigitsAfterComma(new_man, base, max_digit_after_comma, remove_trailing_zeroes, decimal_point); ToString_CorrectDigitsAfterComma<string_type, char_type>(new_man, conv);
} }
@ -3499,20 +3516,18 @@ private:
*/ */
template<class string_type, class char_type> template<class string_type, class char_type>
void ToString_SetCommaAndExponent_Scientific( string_type & new_man, void ToString_SetCommaAndExponent_Scientific( string_type & new_man,
uint base, const Conv & conv,
Int<exp+1> & scientific_exp, Int<exp+1> & scientific_exp ) const
sint max_digit_after_comma,
bool remove_trailing_zeroes,
char_type decimal_point) const
{ {
if( new_man.empty() ) if( new_man.empty() )
return; return;
new_man.insert( new_man.begin()+1, decimal_point ); new_man.insert( new_man.begin()+1, static_cast<char_type>(conv.comma) );
ToString_CorrectDigitsAfterComma(new_man, base, max_digit_after_comma, remove_trailing_zeroes, decimal_point); ToString_CorrectDigitsAfterComma<string_type, char_type>(new_man, conv);
ToString_Group_man<string_type, char_type>(new_man, conv);
if( base == 10 )
if( conv.base == 10 )
{ {
new_man += 'e'; new_man += 'e';
@ -3527,7 +3542,7 @@ private:
} }
string_type temp_exp; string_type temp_exp;
scientific_exp.ToString( temp_exp, base ); scientific_exp.ToString( temp_exp, conv.base );
new_man += temp_exp; new_man += temp_exp;
} }
@ -3537,17 +3552,84 @@ private:
an auxiliary method for converting into the string an auxiliary method for converting into the string
*/ */
template<class string_type, class char_type> template<class string_type, class char_type>
void ToString_CorrectDigitsAfterComma( string_type & new_man, void ToString_Group_man(string_type & new_man, const Conv & conv) const
uint base,
sint max_digit_after_comma,
bool remove_trailing_zeroes,
char_type decimal_point) const
{ {
if( max_digit_after_comma >= 0 ) typedef typename string_type::size_type StrSize;
ToString_CorrectDigitsAfterComma_Round(new_man, base, max_digit_after_comma, decimal_point);
if( remove_trailing_zeroes ) if( conv.group == 0 )
ToString_CorrectDigitsAfterComma_CutOffZeroCharacters(new_man, decimal_point); return;
// first we're looking for the comma operator
StrSize index = new_man.find(static_cast<char_type>(conv.comma), 0);
if( index == string_type::npos )
index = new_man.size();
ToString_Group_man_before_comma<string_type, char_type>(new_man, conv, index);
ToString_Group_man_after_comma<string_type, char_type>(new_man, conv, index+1);
}
/*!
an auxiliary method for converting into the string
*/
template<class string_type, class char_type>
void ToString_Group_man_before_comma( string_type & new_man, const Conv & conv,
typename string_type::size_type & index) const
{
typedef typename string_type::size_type StrSize;
uint group = 0;
StrSize i = index;
// adding group characters before the comma operator
// i>0 because on the first position we don't put any additional grouping characters
for( ; i>0 ; --i, ++group)
{
if( group >= 3 )
{
group = 0;
new_man.insert(i, 1, static_cast<char_type>(conv.group));
++index;
}
}
}
/*!
an auxiliary method for converting into the string
*/
template<class string_type, class char_type>
void ToString_Group_man_after_comma(string_type & new_man, const Conv & conv,
typename string_type::size_type index) const
{
uint group = 0;
for( ; index<new_man.size() ; ++index, ++group)
{
if( group >= 3 )
{
group = 0;
new_man.insert(index, 1, static_cast<char_type>(conv.group));
++index;
}
}
}
/*!
an auxiliary method for converting into the string
*/
template<class string_type, class char_type>
void ToString_CorrectDigitsAfterComma( string_type & new_man,
const Conv & conv ) const
{
if( conv.comma_digits >= 0 )
ToString_CorrectDigitsAfterComma_Round<string_type, char_type>(new_man, conv);
if( conv.trim_zeroes )
ToString_CorrectDigitsAfterComma_CutOffZeroCharacters<string_type, char_type>(new_man, conv);
} }
@ -3557,7 +3639,7 @@ private:
template<class string_type, class char_type> template<class string_type, class char_type>
void ToString_CorrectDigitsAfterComma_CutOffZeroCharacters( void ToString_CorrectDigitsAfterComma_CutOffZeroCharacters(
string_type & new_man, string_type & new_man,
char_type decimal_point) const const Conv & conv) const
{ {
// minimum two characters // minimum two characters
if( new_man.length() < 2 ) if( new_man.length() < 2 )
@ -3575,12 +3657,12 @@ private:
// we must have a comma // we must have a comma
// (the comma can be removed by ToString_CorrectDigitsAfterComma_Round // (the comma can be removed by ToString_CorrectDigitsAfterComma_Round
// which is called before) // which is called before)
if( new_man.find_last_of(decimal_point, i) == string_type::npos ) if( new_man.find_last_of(static_cast<char_type>(conv.comma), i) == string_type::npos )
return; return;
// if directly before the first zero is the comma operator // if directly before the first zero is the comma operator
// we're cutting it as well // we're cutting it as well
if( i>0 && new_man[i]==decimal_point ) if( i>0 && new_man[i]==static_cast<char_type>(conv.comma) )
--i; --i;
new_man.erase(i+1, new_man.length()-i-1); new_man.erase(i+1, new_man.length()-i-1);
@ -3593,12 +3675,12 @@ private:
template<class string_type, class char_type> template<class string_type, class char_type>
void ToString_CorrectDigitsAfterComma_Round( void ToString_CorrectDigitsAfterComma_Round(
string_type & new_man, string_type & new_man,
uint base, const Conv & conv ) const
sint max_digit_after_comma,
char_type decimal_point) const
{ {
typedef typename string_type::size_type StrSize;
// first we're looking for the comma operator // first we're looking for the comma operator
typename string_type::size_type index = new_man.find(decimal_point, 0); StrSize index = new_man.find(static_cast<char_type>(conv.comma), 0);
if( index == string_type::npos ) if( index == string_type::npos )
// nothing was found (actually there can't be this situation) // nothing was found (actually there can't be this situation)
@ -3607,45 +3689,141 @@ private:
// we're calculating how many digits there are at the end (after the comma) // we're calculating how many digits there are at the end (after the comma)
// 'after_comma' will be greater than zero because at the end // 'after_comma' will be greater than zero because at the end
// we have at least one digit // we have at least one digit
typename string_type::size_type after_comma = new_man.length() - index - 1; StrSize after_comma = new_man.length() - index - 1;
// if 'max_digit_after_comma' is greater than 'after_comma' (or equal) // if 'max_digit_after_comma' is greater than 'after_comma' (or equal)
// we don't have anything for cutting // we don't have anything for cutting
if( static_cast<typename string_type::size_type>(max_digit_after_comma) >= after_comma ) if( static_cast<StrSize>(conv.comma_digits) >= after_comma )
return; return;
uint last_digit = Misc::CharToDigit( new_man[ index + max_digit_after_comma + 1 ], base ); uint last_digit = Misc::CharToDigit( new_man[ index + conv.comma_digits + 1 ], conv.base );
// we're cutting the rest of the string // we're cutting the rest of the string
new_man.erase(index + max_digit_after_comma + 1, after_comma - max_digit_after_comma); new_man.erase(index + conv.comma_digits + 1, after_comma - conv.comma_digits);
if( max_digit_after_comma == 0 ) if( conv.comma_digits == 0 )
{ {
// we're cutting the comma operator as well // we're cutting the comma operator as well
// (it's not needed now because we've cut the whole rest after the comma) // (it's not needed now because we've cut the whole rest after the comma)
new_man.erase(index, 1); new_man.erase(index, 1);
} }
if( last_digit >= base / 2 ) if( last_digit >= conv.base / 2 )
// we must round here // we must round here
ToString_RoundMantissa_AddOneIntoMantissa(new_man, base, decimal_point); ToString_RoundMantissa_AddOneIntoMantissa<string_type, char_type>(new_man, conv);
} }
public:
/*!
a method for converting a string into its value
it returns 1 if the value is too big -- we cannot pass it into the range
of our class Big<exp,man> (or if the base is incorrect)
that means only digits before the comma operator can make this value too big,
all digits after the comma we can ignore
'source' - pointer to the string for parsing
if 'after_source' is set that when this method finishes
it sets the pointer to the new first character after parsed value
'value_read' - if the pointer is provided that means the value_read will be true
only when a value has been actually read, there can be situation where only such
a string '-' or '+' will be parsed -- 'after_source' will be different from 'source' but
no value has been read (there are no digits)
on other words if 'value_read' is true -- there is at least one digit in the string
*/
uint FromString(const char * source, uint base = 10, const char ** after_source = 0, bool * value_read = 0)
{
Conv conv;
conv.base = base;
return FromStringBase(source, conv, after_source, value_read);
}
/*!
a method for converting a string into its value
*/
uint FromString(const wchar_t * source, uint base = 10, const wchar_t ** after_source = 0, bool * value_read = 0)
{
Conv conv;
conv.base = base;
return FromStringBase(source, conv, after_source, value_read);
}
/*!
a method for converting a string into its value
*/
uint FromString(const char * source, const Conv & conv, const char ** after_source = 0, bool * value_read = 0)
{
return FromStringBase(source, conv, after_source, value_read);
}
/*!
a method for converting a string into its value
*/
uint FromString(const wchar_t * source, const Conv & conv, const wchar_t ** after_source = 0, bool * value_read = 0)
{
return FromStringBase(source, conv, after_source, value_read);
}
/*!
a method for converting a string into its value
*/
uint FromString(const std::string & string, uint base = 10, const wchar_t ** after_source = 0, bool * value_read = 0)
{
return FromString(string.c_str(), base, after_source, value_read);
}
/*!
a method for converting a string into its value
*/
uint FromString(const std::wstring & string, uint base = 10, const wchar_t ** after_source = 0, bool * value_read = 0)
{
return FromString(string.c_str(), base, after_source, value_read);
}
/*!
a method for converting a string into its value
*/
uint FromString(const std::string & string, const Conv & conv, const wchar_t ** after_source = 0, bool * value_read = 0)
{
return FromString(string.c_str(), conv, after_source, value_read);
}
/*!
a method for converting a string into its value
*/
uint FromString(const std::wstring & string, const Conv & conv, const wchar_t ** after_source = 0, bool * value_read = 0)
{
return FromString(string.c_str(), conv, after_source, value_read);
}
private: private:
/*! /*!
an auxiliary method for converting from a string an auxiliary method for converting from a string
*/ */
template<class char_type> template<class char_type>
uint FromStringBase(const char_type * source, uint base = 10, const char_type ** after_source = 0, bool * value_read = 0) uint FromStringBase(const char_type * source, const Conv & conv, const char_type ** after_source = 0, bool * value_read = 0)
{ {
bool is_sign; bool is_sign;
bool value_read_temp = false; bool value_read_temp = false;
if( base<2 || base>16 ) if( conv.base<2 || conv.base>16 )
{ {
SetNan(); SetNan();
@ -3661,12 +3839,12 @@ private:
SetZero(); SetZero();
FromString_TestSign( source, is_sign ); FromString_TestSign( source, is_sign );
uint c = FromString_ReadPartBeforeComma( source, base, value_read_temp ); uint c = FromString_ReadPartBeforeComma( source, conv, value_read_temp );
if( FromString_TestCommaOperator(source) ) if( FromString_TestCommaOperator(source, conv) )
c += FromString_ReadPartAfterComma( source, base, value_read_temp ); c += FromString_ReadPartAfterComma( source, conv, value_read_temp );
if( value_read_temp && base == 10 ) if( value_read_temp && conv.base == 10 )
c += FromString_ReadScientificIfExists( source ); c += FromString_ReadScientificIfExists( source );
if( is_sign && !IsZero() ) if( is_sign && !IsZero() )
@ -3682,48 +3860,6 @@ private:
} }
public:
/*!
a method for converting a string into its value
it returns 1 if the value will be too big -- we cannot pass it into the range
of our class Big<exp,man> (or if the base is incorrect)
that means only digits before the comma operator can make this value too big,
all digits after the comma we can ignore
'source' - pointer to the string for parsing
'const char*' or 'const wchar_t*'
if 'after_source' is set that when this method finishes
it sets the pointer to the new first character after parsed value
'value_read' - if the pointer is provided that means the value_read will be true
only when a value has been actually read, there can be situation where only such
a string '-' or '+' will be parsed -- 'after_source' will be different from 'source' but
no value has been read (there are no digits)
on other words if 'value_read' is true -- there is at least one digit in the string
*/
uint FromString(const char * source, uint base = 10, const char ** after_source = 0, bool * value_read = 0)
{
return FromStringBase(source, base, after_source, value_read);
}
/*!
a method for converting a string into its value
*/
uint FromString(const wchar_t * source, uint base = 10, const wchar_t ** after_source = 0, bool * value_read = 0)
{
return FromStringBase(source, base, after_source, value_read);
}
private:
/*! /*!
we're testing whether the value is with the sign we're testing whether the value is with the sign
@ -3753,10 +3889,10 @@ private:
we're testing whether there's a comma operator we're testing whether there's a comma operator
*/ */
template<class char_type> template<class char_type>
bool FromString_TestCommaOperator(const char_type * & source) bool FromString_TestCommaOperator(const char_type * & source, const Conv & conv)
{ {
if( (*source == TTMATH_COMMA_CHARACTER_1) || if( (*source == static_cast<char_type>(conv.comma)) ||
(*source == TTMATH_COMMA_CHARACTER_2 && TTMATH_COMMA_CHARACTER_2 != 0 ) ) (*source == static_cast<char_type>(conv.comma2) && conv.comma2 != 0 ) )
{ {
++source; ++source;
@ -3772,18 +3908,25 @@ private:
(before the comma operator) (before the comma operator)
*/ */
template<class char_type> template<class char_type>
uint FromString_ReadPartBeforeComma( const char_type * & source, uint base, bool & value_read ) uint FromString_ReadPartBeforeComma( const char_type * & source, const Conv & conv, bool & value_read )
{ {
sint character; sint character;
Big<exp, man> temp; Big<exp, man> temp;
Big<exp, man> base_( base ); Big<exp, man> base_( conv.base );
Misc::SkipWhiteCharacters( source ); Misc::SkipWhiteCharacters( source );
for( ; (character=Misc::CharToDigit(*source, base)) != -1 ; ++source ) for( ; true ; ++source )
{ {
value_read = true; if( conv.group!=0 && *source==static_cast<char>(conv.group) )
continue;
character = Misc::CharToDigit(*source, conv.base);
if( character == -1 )
break;
value_read = true;
temp = character; temp = character;
if( Mul(base_) ) if( Mul(base_) )
@ -3802,11 +3945,11 @@ private:
(after the comma operator) (after the comma operator)
*/ */
template<class char_type> template<class char_type>
uint FromString_ReadPartAfterComma( const char_type * & source, uint base, bool & value_read ) uint FromString_ReadPartAfterComma( const char_type * & source, const Conv & conv, bool & value_read )
{ {
sint character; sint character;
uint c = 0, index = 1; uint c = 0, index = 1;
Big<exp, man> part, power, old_value, base_( base ); Big<exp, man> part, power, old_value, base_( conv.base );
// we don't remove any white characters here // we don't remove any white characters here
@ -3816,8 +3959,16 @@ private:
power.SetOne(); power.SetOne();
for( ; (character=Misc::CharToDigit(*source, base)) != -1 ; ++source, ++index ) for( ; true ; ++source, ++index )
{ {
if( conv.group!=0 && *source==static_cast<char>(conv.group) )
continue;
character = Misc::CharToDigit(*source, conv.base);
if( character == -1 )
break;
value_read = true; value_read = true;
part = character; part = character;
@ -3849,7 +4000,7 @@ private:
// we could break the parsing somewhere in the middle of the string, // we could break the parsing somewhere in the middle of the string,
// but the result (value) still can be good // but the result (value) still can be good
// we should set a correct value of 'source' now // we should set a correct value of 'source' now
for( ; Misc::CharToDigit(*source, base) != -1 ; ++source ); for( ; Misc::CharToDigit(*source, conv.base) != -1 ; ++source );
return (c==0)? 0 : 1; return (c==0)? 0 : 1;
} }
@ -3963,24 +4114,6 @@ private:
public: public:
/*!
a method for converting a string into its value
*/
uint FromString(const std::string & string, uint base = 10)
{
return FromString( string.c_str(), base );
}
/*!
a method for converting a string into its value
*/
uint FromString(const std::wstring & string, uint base = 10)
{
return FromString( string.c_str(), base );
}
/*! /*!
a constructor for converting a string into this class a constructor for converting a string into this class
*/ */
@ -4564,8 +4697,7 @@ private:
// we're reading only digits (base=10) and only one comma operator // we're reading only digits (base=10) and only one comma operator
for( ; s.good() ; z=static_cast<char_type>(s.get()) ) for( ; s.good() ; z=static_cast<char_type>(s.get()) )
{ {
if( z == TTMATH_COMMA_CHARACTER_1 || if( z=='.' || z==',' )
( z == TTMATH_COMMA_CHARACTER_2 && TTMATH_COMMA_CHARACTER_2 != 0 ) )
{ {
if( was_comma || was_e ) if( was_comma || was_e )
// second comma operator or comma operator after 'e' character // second comma operator or comma operator after 'e' character

View File

@ -110,8 +110,7 @@ namespace ttmath
for example a correct input string can be: for example a correct input string can be:
"1" "1"
"2.1234" "2.1234"
"2,1234" (they are the same, we can either use a comma or a dot in values) "2,1234" (they are the same, by default we can either use a comma or a dot)
(look at the macro TTMATH_COMMA_CHARACTER_2)
"1 + 2" "1 + 2"
"(1 + 2) * 3" "(1 + 2) * 3"
"pi" "pi"
@ -438,6 +437,28 @@ CGamma<ValueType> cgamma;
std::string wide_to_ansi; std::string wide_to_ansi;
/*!
group character (used when parsing)
default zero (not used)
*/
int group;
/*!
characters used as a comma
default: '.' and ','
comma2 can be zero (it means it is not used)
*/
int comma, comma2;
/*!
an additional character used as a separator between function parameters
(semicolon is used always)
*/
int param_sep;
/*! /*!
true if something was calculated (at least one mathematical operator was used or a function or a variable) true if something was calculated (at least one mathematical operator was used or a function or a variable)
*/ */
@ -1713,9 +1734,15 @@ void ReadValue(Item & result, int reading_base)
{ {
const char * new_stack_pointer; const char * new_stack_pointer;
bool value_read; bool value_read;
Conv conv;
uint carry = result.value.FromString(pstring, reading_base, &new_stack_pointer, &value_read); conv.base = base;
pstring = new_stack_pointer; conv.comma = comma;
conv.comma2 = comma2;
conv.group = group;
uint carry = result.value.FromString(pstring, conv, &new_stack_pointer, &value_read);
pstring = new_stack_pointer;
if( carry ) if( carry )
Error( err_overflow ); Error( err_overflow );
@ -1730,10 +1757,10 @@ bool value_read;
*/ */
bool ValueStarts(int character, int base) bool ValueStarts(int character, int base)
{ {
if( character == TTMATH_COMMA_CHARACTER_1 ) if( character == comma )
return true; return true;
if( TTMATH_COMMA_CHARACTER_2 != 0 && character == TTMATH_COMMA_CHARACTER_2 ) if( comma2!=0 && character==comma2 )
return true; return true;
if( Misc::CharToDigit(character, base) != -1 ) if( Misc::CharToDigit(character, base) != -1 )
@ -1989,7 +2016,7 @@ int ReadOperator(Item & result)
++pstring; ++pstring;
} }
else else
if( *pstring == ';' ) if( *pstring == ';' || (param_sep!=0 && *pstring==param_sep) )
{ {
result.type = Item::semicolon; result.type = Item::semicolon;
++pstring; ++pstring;
@ -2464,12 +2491,16 @@ public:
Parser(): default_stack_size(100) Parser(): default_stack_size(100)
{ {
pstop_calculating = 0; pstop_calculating = 0;
puser_variables = 0; puser_variables = 0;
puser_functions = 0; puser_functions = 0;
pfunction_local_variables = 0; pfunction_local_variables = 0;
base = 10; base = 10;
deg_rad_grad = 1; deg_rad_grad = 1;
error = err_ok; error = err_ok;
group = 0;
comma = '.';
comma2 = ',';
param_sep = 0;
CreateFunctionsTable(); CreateFunctionsTable();
CreateVariablesTable(); CreateVariablesTable();
@ -2488,7 +2519,11 @@ Parser<ValueType> & operator=(const Parser<ValueType> & p)
pfunction_local_variables = 0; pfunction_local_variables = 0;
base = p.base; base = p.base;
deg_rad_grad = p.deg_rad_grad; deg_rad_grad = p.deg_rad_grad;
error = err_ok; error = p.error;
group = p.group;
comma = p.comma;
comma2 = p.comma2;
param_sep = p.param_sep;
/* /*
we don't have to call 'CreateFunctionsTable()' etc. we don't have to call 'CreateFunctionsTable()' etc.
@ -2515,7 +2550,8 @@ Parser(const Parser<ValueType> & p): default_stack_size(p.default_stack_size)
/*! /*!
the new base of mathematic system the new base of mathematic system
default is 10
*/ */
void SetBase(int b) void SetBase(int b)
{ {
@ -2570,6 +2606,39 @@ void SetFunctions(const Objects * pf)
} }
/*!
setting the group character
default zero (not used)
*/
void SetGroup(int g)
{
group = g;
}
/*!
setting the main comma operator and the additional comma operator
the additional operator can be zero (which means it is not used)
default are: '.' and ','
*/
void SetComma(int c, int c2 = 0)
{
comma = c;
comma2 = c2;
}
/*!
setting an additional character which is used as a parameters separator
the main parameters separator is a semicolon (is used always)
this character is used also as a global separator
*/
void SetParamSep(int s)
{
param_sep = s;
}
/*! /*!
the main method using for parsing string the main method using for parsing string

View File

@ -233,23 +233,6 @@ namespace ttmath
#endif #endif
#endif #endif
/*!
characters which represent the comma operator
TTMATH_COMMA_CHARACTER_1 is used in reading (parsing) and in writing (default, can be overwritten in ToString() function)
TTMATH_COMMA_CHARACTER_2 can be used in reading as an auxiliary comma character
that means you can input values for example 1.2345 and 1,2345 as well
if you don't want it just put 0 there e.g.
#define TTMATH_COMMA_CHARACTER_2 0
then only TTMATH_COMMA_CHARACTER_1 will be used
don't put there any special character which is used by the parser
(for example a semicolon ';' shouldn't be there)
*/
#define TTMATH_COMMA_CHARACTER_1 '.'
#define TTMATH_COMMA_CHARACTER_2 ','
/*! /*!
@ -336,6 +319,114 @@ namespace ttmath
}; };
/*!
this struct is used when converting to/from a string
*/
struct Conv
{
/*!
base (radix) on which the value will be shown (or read)
default: 10
*/
uint base;
/*!
used only in Big::ToString()
if true the value will be always shown in the scientific mode, e.g: 123e+30
default: false
*/
bool scient;
/*!
used only in Big::ToString()
if scient is false then the value will be print in the scientific mode
only if the exponent is greater than scien_from
default: 15
*/
sint scient_from;
/*!
used only in Big::ToString()
tells how many digits after comma are possible
default: -1 which means all digits are printed
set it to zero if you want integer value only
for example when the value is:
12.345678 and comma_digit is 4
then the result will be
12.3457 (the last digit was rounded)
*/
sint comma_digits;
/*!
if true that not mattered digits in the mantissa will be cut off
(zero characters at the end -- after the comma operator)
e.g. 1234,78000 will be: 1234,78
default: true
*/
bool trim_zeroes;
/*!
the main comma operator (used when reading and writing)
default is a dot '.'
*/
uint comma;
/*!
additional comma operator (used only when reading)
if you don't want it just set it to zero
default is a comma ','
this allowes you to convert from a value:
123.45 as well as from 123,45
*/
uint comma2;
/*!
it sets the character which is used for grouping
if group=' ' then: 1234,56789 will be printed as: 1 234,567 89
if you don't want grouping just set it to zero (which is default)
*/
uint group;
/*!
*/
uint group_exp; // not implemented yet
/*!
*/
bool group_multiple; // not implemented yet
Conv()
{
// default values
base = 10;
scient = false;
scient_from = 15;
comma_digits = -1; // !! change to 'round' ?
trim_zeroes = true;
comma = '.';
comma2 = ',';
group = 0;
group_exp = 0;
group_multiple = true;
}
};
/*! /*!
this simple class can be used in multithreading model this simple class can be used in multithreading model
(you can write your own class derived from this one) (you can write your own class derived from this one)