diff --git a/CHANGELOG b/CHANGELOG index 88113b8..5931319 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -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: Big::IsInteger() 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) * added: to the parser: operator percentage 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: Big::Div(ss2) Big::Mod(ss2) @@ -36,7 +52,9 @@ Version 0.9.0 prerelease (2009.10.25): * removed: Parser<>::SetFactorialMax() method the factorial() is such a fast now that we don't need the method longer * 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): * fixed: UInt::SetBitInWord(uint & value, uint bit) set 1 if the bit was diff --git a/ttmath/ttmathbig.h b/ttmath/ttmathbig.h index 018f822..1cddf0e 100644 --- a/ttmath/ttmathbig.h +++ b/ttmath/ttmathbig.h @@ -2809,82 +2809,114 @@ public: /*! - a method for converting the value into a string with a base equal 'base' - - 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 + a method for converting into a string + struct Conv is defined in ttmathtypes.h, look there for more information about parameters output: return 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) */ uint ToString( std::string & result, - uint base = 10, - bool always_scientific = false, - sint when_scientific = 15, - sint max_digit_after_comma = -1, - bool remove_trailing_zeroes = true, - char decimal_point = TTMATH_COMMA_CHARACTER_1 ) const + uint base = 10, + bool scient = false, + sint scient_from = 15, + sint comma_digits = -1, + bool trim_zeroes = true, + wchar_t comma = '.' ) const { - return ToStringBase(result, base, always_scientific, when_scientific, - max_digit_after_comma, remove_trailing_zeroes, decimal_point); + Conv conv; + + 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(comma); + + return ToStringBase(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 base = 10, - bool always_scientific = false, - sint when_scientific = 15, - sint max_digit_after_comma = -1, - bool remove_trailing_zeroes = true, - wchar_t decimal_point = TTMATH_COMMA_CHARACTER_1 ) const + uint base = 10, + bool scient = false, + sint scient_from = 15, + sint comma_digits = -1, + bool trim_zeroes = true, + wchar_t comma = '.' ) const { - return ToStringBase(result, base, always_scientific, when_scientific, - max_digit_after_comma, remove_trailing_zeroes, decimal_point); + Conv conv; + + 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(comma); + + return ToStringBase(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(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(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(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: + /*! + an auxiliary method for converting into the string + */ template - uint ToStringBase( string_type & result, - uint base, - bool always_scientific, - sint when_scientific, - sint max_digit_after_comma, - bool remove_trailing_zeroes, - char_type decimal_point) const + uint ToStringBase(string_type & result, const Conv & conv) const { static char error_overflow_msg[] = "overflow"; static char error_nan_msg[] = "NaN"; @@ -2896,7 +2928,7 @@ private: return 0; } - if( base<2 || base>16 ) + if( conv.base<2 || conv.base>16 ) { Misc::AssignString(result, error_overflow_msg); return 1; @@ -2926,7 +2958,7 @@ private: */ Int new_exp; - if( ToString_CreateNewMantissaAndExponent(result, base, new_exp) ) + if( ToString_CreateNewMantissaAndExponent(result, conv, new_exp) ) { Misc::AssignString(result, error_overflow_msg); return 1; @@ -2936,16 +2968,14 @@ private: 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) */ - if( base!=2 && base!=4 && base!=8 && base!=16 ) - if( ToString_RoundMantissa(result, base, new_exp, decimal_point) ) + if( conv.base!=2 && conv.base!=4 && conv.base!=8 && conv.base!=16 ) + if( ToString_RoundMantissa(result, conv, new_exp) ) { Misc::AssignString(result, error_overflow_msg); return 1; } - if( ToString_SetCommaAndExponent( result, base, new_exp, always_scientific, - when_scientific, max_digit_after_comma, - remove_trailing_zeroes, decimal_point ) ) + if( ToString_SetCommaAndExponent(result, conv, new_exp) ) { Misc::AssignString(result, error_overflow_msg); return 1; @@ -2959,8 +2989,6 @@ private: } -private: - /*! 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: 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 */ template - uint ToString_CreateNewMantissaAndExponent( string_type & new_man, uint base, + uint ToString_CreateNewMantissaAndExponent( string_type & new_man, const Conv & conv, Int & new_exp) const { uint c = 0; - if(base<2 || base>16) + if( conv.base<2 || conv.base>16 ) return 1; // the speciality for base equal 2 - if( base == 2 ) + if( conv.base == 2 ) return ToString_CreateNewMantissaAndExponent_Base2(new_man, new_exp); // this = mantissa * 2^exponent @@ -3054,7 +3082,7 @@ private: // new_exp_ = [log base (2^exponent)] + 1 Big 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(); temp.SetOne(); c += new_exp_.Add( temp ); @@ -3067,7 +3095,7 @@ private: c += new_exp_.ToInt(new_exp); // base_ = base - Big base_(base); + Big base_(conv.base); // base_ = base_ ^ new_exp_ c += base_.Pow( new_exp_ ); @@ -3089,7 +3117,7 @@ private: // (temp.Div( base_ )) the value of exponent should be equal zero or // minimum smaller than zero then we've got the mantissa which has // 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 // shouldn't have had a carry @@ -3307,7 +3335,7 @@ private: (it's used in systems where the base is different from 2) */ template - uint ToString_RoundMantissa(string_type & new_man, uint base, Int & new_exp, char_type decimal_point) const + uint ToString_RoundMantissa(string_type & new_man, const Conv & conv, Int & new_exp) const { // we must have minimum two characters if( new_man.length() < 2 ) @@ -3322,8 +3350,8 @@ private: // if the last character is greater or equal 'base/2' // we'll add one into the new mantissa - if( digit >= base / 2 ) - ToString_RoundMantissa_AddOneIntoMantissa(new_man, base, decimal_point); + if( digit >= conv.base / 2 ) + ToString_RoundMantissa_AddOneIntoMantissa(new_man, conv); return carry; } @@ -3335,7 +3363,7 @@ private: this method addes one into the new mantissa */ template - 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() ) return; @@ -3348,13 +3376,13 @@ private: // we can have the comma as well because // we're using this method later in ToString_CorrectDigitsAfterComma_Round() // (we're only ignoring it) - if( new_man[i] == decimal_point ) + if( new_man[i] == static_cast(conv.comma) ) continue; // we're adding one uint digit = Misc::CharToDigit( new_man[i] ) + 1; - if( digit == base ) + if( digit == conv.base ) digit = 0; else was_carry = false; @@ -3374,13 +3402,7 @@ private: into the string */ template - uint ToString_SetCommaAndExponent( string_type & new_man, uint base, - Int & new_exp, - bool always_scientific, - sint when_scientific, - sint max_digit_after_comma, - bool remove_trailing_zeroes, - char_type decimal_point) const + uint ToString_SetCommaAndExponent(string_type & new_man, const Conv & conv, Int & new_exp) const { uint carry = 0; @@ -3399,18 +3421,19 @@ private: // there shouldn't have been a carry because we're using // a greater type -- 'Int' instead of 'Int' - if( !always_scientific ) + bool print_scientific = conv.scient; + + if( !print_scientific ) { - if( scientific_exp > when_scientific || scientific_exp < -sint(when_scientific) ) - always_scientific = true; + if( scientific_exp > conv.scient_from || scientific_exp < -sint(conv.scient_from) ) + print_scientific = true; } - // 'always_scientific' could be changed - if( !always_scientific ) - ToString_SetCommaAndExponent_Normal(new_man, base, new_exp, max_digit_after_comma, remove_trailing_zeroes, decimal_point); + if( !print_scientific ) + ToString_SetCommaAndExponent_Normal(new_man, conv, new_exp); else // 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(new_man, conv, scientific_exp); return (carry==0)? 0 : 1; } @@ -3420,25 +3443,22 @@ private: an auxiliary method for converting into the string */ template - void ToString_SetCommaAndExponent_Normal( - string_type & new_man, - uint base, - Int & new_exp, - sint max_digit_after_comma, - bool remove_trailing_zeroes, - char_type decimal_point) const + void ToString_SetCommaAndExponent_Normal(string_type & new_man, const Conv & conv, Int & new_exp ) const { - if( !new_exp.IsSign() ) //if( new_exp >= 0 ) - return ToString_SetCommaAndExponent_Normal_AddingZero(new_man, new_exp); + if( !new_exp.IsSign() ) // it means: if( new_exp >= 0 ) + ToString_SetCommaAndExponent_Normal_AddingZero(new_man, new_exp); else - return ToString_SetCommaAndExponent_Normal_SetCommaInside(new_man, base, new_exp, max_digit_after_comma, remove_trailing_zeroes, decimal_point); + ToString_SetCommaAndExponent_Normal_SetCommaInside(new_man, conv, new_exp); + + + ToString_Group_man(new_man, conv); } /*! an auxiliary method for converting into the string */ - template + template void ToString_SetCommaAndExponent_Normal_AddingZero(string_type & new_man, Int & new_exp) const { @@ -3460,12 +3480,9 @@ private: */ template void ToString_SetCommaAndExponent_Normal_SetCommaInside( - string_type & new_man, - uint base, - Int & new_exp, - sint max_digit_after_comma, - bool remove_trailing_zeroes, - char_type decimal_point) const + string_type & new_man, + const Conv & conv, + Int & new_exp ) const { // new_exp is < 0 @@ -3477,7 +3494,7 @@ private: // we're setting the comma within the mantissa sint index = new_man_len - e; - new_man.insert( new_man.begin() + index, decimal_point); + new_man.insert( new_man.begin() + index, static_cast(conv.comma)); } else { @@ -3486,11 +3503,11 @@ private: uint how_many = e - new_man_len; 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(conv.comma)); new_man.insert(0, man_temp); } - ToString_CorrectDigitsAfterComma(new_man, base, max_digit_after_comma, remove_trailing_zeroes, decimal_point); + ToString_CorrectDigitsAfterComma(new_man, conv); } @@ -3499,20 +3516,18 @@ private: */ template void ToString_SetCommaAndExponent_Scientific( string_type & new_man, - uint base, - Int & scientific_exp, - sint max_digit_after_comma, - bool remove_trailing_zeroes, - char_type decimal_point) const + const Conv & conv, + Int & scientific_exp ) const { if( new_man.empty() ) return; - new_man.insert( new_man.begin()+1, decimal_point ); + new_man.insert( new_man.begin()+1, static_cast(conv.comma) ); - ToString_CorrectDigitsAfterComma(new_man, base, max_digit_after_comma, remove_trailing_zeroes, decimal_point); - - if( base == 10 ) + ToString_CorrectDigitsAfterComma(new_man, conv); + ToString_Group_man(new_man, conv); + + if( conv.base == 10 ) { new_man += 'e'; @@ -3527,7 +3542,7 @@ private: } string_type temp_exp; - scientific_exp.ToString( temp_exp, base ); + scientific_exp.ToString( temp_exp, conv.base ); new_man += temp_exp; } @@ -3537,17 +3552,84 @@ private: an auxiliary method for converting into the string */ template - void ToString_CorrectDigitsAfterComma( string_type & new_man, - uint base, - sint max_digit_after_comma, - bool remove_trailing_zeroes, - char_type decimal_point) const + void ToString_Group_man(string_type & new_man, const Conv & conv) const { - if( max_digit_after_comma >= 0 ) - ToString_CorrectDigitsAfterComma_Round(new_man, base, max_digit_after_comma, decimal_point); + typedef typename string_type::size_type StrSize; - if( remove_trailing_zeroes ) - ToString_CorrectDigitsAfterComma_CutOffZeroCharacters(new_man, decimal_point); + if( conv.group == 0 ) + return; + + // first we're looking for the comma operator + StrSize index = new_man.find(static_cast(conv.comma), 0); + + if( index == string_type::npos ) + index = new_man.size(); + + ToString_Group_man_before_comma(new_man, conv, index); + ToString_Group_man_after_comma(new_man, conv, index+1); + } + + + + /*! + an auxiliary method for converting into the string + */ + template + 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(conv.group)); + ++index; + } + } + } + + + /*! + an auxiliary method for converting into the string + */ + template + 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= 3 ) + { + group = 0; + new_man.insert(index, 1, static_cast(conv.group)); + ++index; + } + } + } + + + /*! + an auxiliary method for converting into the string + */ + template + void ToString_CorrectDigitsAfterComma( string_type & new_man, + const Conv & conv ) const + { + if( conv.comma_digits >= 0 ) + ToString_CorrectDigitsAfterComma_Round(new_man, conv); + + if( conv.trim_zeroes ) + ToString_CorrectDigitsAfterComma_CutOffZeroCharacters(new_man, conv); } @@ -3557,7 +3639,7 @@ private: template void ToString_CorrectDigitsAfterComma_CutOffZeroCharacters( string_type & new_man, - char_type decimal_point) const + const Conv & conv) const { // minimum two characters if( new_man.length() < 2 ) @@ -3575,12 +3657,12 @@ private: // we must have a comma // (the comma can be removed by ToString_CorrectDigitsAfterComma_Round // which is called before) - if( new_man.find_last_of(decimal_point, i) == string_type::npos ) + if( new_man.find_last_of(static_cast(conv.comma), i) == string_type::npos ) return; // if directly before the first zero is the comma operator // we're cutting it as well - if( i>0 && new_man[i]==decimal_point ) + if( i>0 && new_man[i]==static_cast(conv.comma) ) --i; new_man.erase(i+1, new_man.length()-i-1); @@ -3593,12 +3675,12 @@ private: template void ToString_CorrectDigitsAfterComma_Round( string_type & new_man, - uint base, - sint max_digit_after_comma, - char_type decimal_point) const + const Conv & conv ) const { + typedef typename string_type::size_type StrSize; + // 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(conv.comma), 0); if( index == string_type::npos ) // 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) // 'after_comma' will be greater than zero because at the end // 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) // we don't have anything for cutting - if( static_cast(max_digit_after_comma) >= after_comma ) + if( static_cast(conv.comma_digits) >= after_comma ) 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 - 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 // (it's not needed now because we've cut the whole rest after the comma) new_man.erase(index, 1); } - if( last_digit >= base / 2 ) + if( last_digit >= conv.base / 2 ) // we must round here - ToString_RoundMantissa_AddOneIntoMantissa(new_man, base, decimal_point); + ToString_RoundMantissa_AddOneIntoMantissa(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 (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: + /*! an auxiliary method for converting from a string */ template - 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 value_read_temp = false; - if( base<2 || base>16 ) + if( conv.base<2 || conv.base>16 ) { SetNan(); @@ -3661,12 +3839,12 @@ private: SetZero(); 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) ) - c += FromString_ReadPartAfterComma( source, base, value_read_temp ); + if( FromString_TestCommaOperator(source, conv) ) + 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 ); 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 (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 @@ -3753,10 +3889,10 @@ private: we're testing whether there's a comma operator */ template - bool FromString_TestCommaOperator(const char_type * & source) + bool FromString_TestCommaOperator(const char_type * & source, const Conv & conv) { - if( (*source == TTMATH_COMMA_CHARACTER_1) || - (*source == TTMATH_COMMA_CHARACTER_2 && TTMATH_COMMA_CHARACTER_2 != 0 ) ) + if( (*source == static_cast(conv.comma)) || + (*source == static_cast(conv.comma2) && conv.comma2 != 0 ) ) { ++source; @@ -3772,18 +3908,25 @@ private: (before the comma operator) */ template - 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; Big temp; - Big base_( base ); + Big base_( conv.base ); Misc::SkipWhiteCharacters( source ); - for( ; (character=Misc::CharToDigit(*source, base)) != -1 ; ++source ) + for( ; true ; ++source ) { - value_read = true; + if( conv.group!=0 && *source==static_cast(conv.group) ) + continue; + character = Misc::CharToDigit(*source, conv.base); + + if( character == -1 ) + break; + + value_read = true; temp = character; if( Mul(base_) ) @@ -3802,11 +3945,11 @@ private: (after the comma operator) */ template - 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; uint c = 0, index = 1; - Big part, power, old_value, base_( base ); + Big part, power, old_value, base_( conv.base ); // we don't remove any white characters here @@ -3816,8 +3959,16 @@ private: power.SetOne(); - for( ; (character=Misc::CharToDigit(*source, base)) != -1 ; ++source, ++index ) + for( ; true ; ++source, ++index ) { + if( conv.group!=0 && *source==static_cast(conv.group) ) + continue; + + character = Misc::CharToDigit(*source, conv.base); + + if( character == -1 ) + break; + value_read = true; part = character; @@ -3849,7 +4000,7 @@ private: // we could break the parsing somewhere in the middle of the string, // but the result (value) still can be good // 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; } @@ -3963,24 +4114,6 @@ private: 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 */ @@ -4564,8 +4697,7 @@ private: // we're reading only digits (base=10) and only one comma operator for( ; s.good() ; z=static_cast(s.get()) ) { - if( z == TTMATH_COMMA_CHARACTER_1 || - ( z == TTMATH_COMMA_CHARACTER_2 && TTMATH_COMMA_CHARACTER_2 != 0 ) ) + if( z=='.' || z==',' ) { if( was_comma || was_e ) // second comma operator or comma operator after 'e' character diff --git a/ttmath/ttmathparser.h b/ttmath/ttmathparser.h index 8acb616..e657a9d 100644 --- a/ttmath/ttmathparser.h +++ b/ttmath/ttmathparser.h @@ -110,8 +110,7 @@ namespace ttmath for example a correct input string can be: "1" "2.1234" - "2,1234" (they are the same, we can either use a comma or a dot in values) - (look at the macro TTMATH_COMMA_CHARACTER_2) + "2,1234" (they are the same, by default we can either use a comma or a dot) "1 + 2" "(1 + 2) * 3" "pi" @@ -438,6 +437,28 @@ CGamma cgamma; 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) */ @@ -1713,9 +1734,15 @@ void ReadValue(Item & result, int reading_base) { const char * new_stack_pointer; bool value_read; +Conv conv; - uint carry = result.value.FromString(pstring, reading_base, &new_stack_pointer, &value_read); - pstring = new_stack_pointer; + conv.base = base; + 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 ) Error( err_overflow ); @@ -1730,10 +1757,10 @@ bool value_read; */ bool ValueStarts(int character, int base) { - if( character == TTMATH_COMMA_CHARACTER_1 ) + if( character == comma ) return true; - if( TTMATH_COMMA_CHARACTER_2 != 0 && character == TTMATH_COMMA_CHARACTER_2 ) + if( comma2!=0 && character==comma2 ) return true; if( Misc::CharToDigit(character, base) != -1 ) @@ -1989,7 +2016,7 @@ int ReadOperator(Item & result) ++pstring; } else - if( *pstring == ';' ) + if( *pstring == ';' || (param_sep!=0 && *pstring==param_sep) ) { result.type = Item::semicolon; ++pstring; @@ -2464,12 +2491,16 @@ public: Parser(): default_stack_size(100) { pstop_calculating = 0; - puser_variables = 0; - puser_functions = 0; + puser_variables = 0; + puser_functions = 0; pfunction_local_variables = 0; - base = 10; - deg_rad_grad = 1; - error = err_ok; + base = 10; + deg_rad_grad = 1; + error = err_ok; + group = 0; + comma = '.'; + comma2 = ','; + param_sep = 0; CreateFunctionsTable(); CreateVariablesTable(); @@ -2488,7 +2519,11 @@ Parser & operator=(const Parser & p) pfunction_local_variables = 0; base = p.base; 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. @@ -2515,7 +2550,8 @@ Parser(const Parser & 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) { @@ -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 diff --git a/ttmath/ttmathtypes.h b/ttmath/ttmathtypes.h index 98399f0..67f0639 100644 --- a/ttmath/ttmathtypes.h +++ b/ttmath/ttmathtypes.h @@ -233,23 +233,6 @@ namespace ttmath #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 (you can write your own class derived from this one)