From d161281a47685f2bc821cc84dbf27a6c45926591 Mon Sep 17 00:00:00 2001 From: Alex Dyachenko Date: Wed, 25 Jun 2014 16:22:37 -0400 Subject: [PATCH] Float IO - Read implementation. Also updated construction and assignment to make all string conversions consistent. MPIR 2.7.0 Section 7.7 Completed. --- .../HugeFloatTests/ConstructionAndDisposal.cs | 49 +++++++++++ .../HugeFloatTests/Conversions.cs | 17 ++++ mpir.net/mpir.net-tests/HugeFloatTests/IO.cs | 4 +- mpir.net/mpir.net/HugeFloat.cpp | 87 +++++++++++++++++-- mpir.net/mpir.net/HugeFloat.h | 43 +++++++-- 5 files changed, 184 insertions(+), 16 deletions(-) diff --git a/mpir.net/mpir.net-tests/HugeFloatTests/ConstructionAndDisposal.cs b/mpir.net/mpir.net-tests/HugeFloatTests/ConstructionAndDisposal.cs index 981da817..f539e961 100644 --- a/mpir.net/mpir.net-tests/HugeFloatTests/ConstructionAndDisposal.cs +++ b/mpir.net/mpir.net-tests/HugeFloatTests/ConstructionAndDisposal.cs @@ -127,6 +127,34 @@ namespace MPIR.Tests.HugeFloatTests } } + [TestMethod] + public void FloatStringConstructorWithAlternativeExponentMarker() + { + var n = "5432109876543212345789.70515331128527330659e3"; + using(var a = new HugeFloat(n)) + { + FloatAssert.AreEqual("5432109876543212345789705.15331128527330659", a); + } + } + + [TestMethod] + public void FloatStringConstructorWithAlternativeExponentMarker2() + { + var n = "5432109876543212345789.70515331128527330659E3"; + using(var a = new HugeFloat(n)) + { + FloatAssert.AreEqual("5432109876543212345789705.15331128527330659", a); + } + } + + [TestMethod] + [ExpectedException(typeof(ArgumentException))] + public void FloatStringConstructorWithAlternativeExponentMarkerInvalid() + { + var n = "5432109876543212345789.70515331128527330659e3"; + var a = new HugeFloat(n, 11); + } + [TestMethod] [ExpectedException(typeof(ArgumentException))] public void FloatStringConstructorInvalid() @@ -144,6 +172,27 @@ namespace MPIR.Tests.HugeFloatTests } } + [TestMethod] + public void FloatStringConstructorHexExponentDecimal() + { + var n = "143210ABCDEF32123457ACDB324.59879@10"; + using(var a = new HugeFloat(n, 16)) + { + Assert.AreEqual("0.143210ABCDEF32123457ACDB32459879@37", a.ToString(16, false, true)); + } + } + + [TestMethod] + public void FloatStringConstructorHexExponentInBase() + { + var n = "143210ABCDEF32123457ACDB324.59879@10"; + using(var a = new HugeFloat(n, 16, false)) + { + Assert.AreEqual("0.143210ABCDEF32123457ACDB32459879@2B", a.ToString(16, false, false)); + Assert.AreEqual("0.143210ABCDEF32123457ACDB32459879@43", a.ToString(16, false, true)); + } + } + [TestMethod] public void FloatConstructorFromExpression() { diff --git a/mpir.net/mpir.net-tests/HugeFloatTests/Conversions.cs b/mpir.net/mpir.net-tests/HugeFloatTests/Conversions.cs index 308afe8e..fdbd41a8 100644 --- a/mpir.net/mpir.net-tests/HugeFloatTests/Conversions.cs +++ b/mpir.net/mpir.net-tests/HugeFloatTests/Conversions.cs @@ -158,6 +158,23 @@ namespace MPIR.Tests.HugeFloatTests } } + [TestMethod] + public void FloatFromStringExpDecimal() + { + using(var a = new HugeFloat()) + { + var n = "0.12354523094527035843ABCDEF54@10"; + + a.SetTo(n, 16); + Assert.AreEqual("0.12354523094527035843ABCDEF54@10", a.ToString(16, false, true)); + Assert.AreEqual("0.12354523094527035843ABCDEF54@A", a.ToString(16, false, false)); + + a.SetTo(n, 16, false); + Assert.AreEqual("0.12354523094527035843ABCDEF54@16", a.ToString(16, false, true)); + Assert.AreEqual("0.12354523094527035843ABCDEF54@10", a.ToString(16, false, false)); + } + } + [TestMethod] [ExpectedException(typeof(ArgumentException))] public void FloatFromInvalidString() diff --git a/mpir.net/mpir.net-tests/HugeFloatTests/IO.cs b/mpir.net/mpir.net-tests/HugeFloatTests/IO.cs index f595ee82..c803a22a 100644 --- a/mpir.net/mpir.net-tests/HugeFloatTests/IO.cs +++ b/mpir.net/mpir.net-tests/HugeFloatTests/IO.cs @@ -45,7 +45,7 @@ namespace MPIR.Tests.HugeFloatTests using (var reader = new StreamReader(ms, Encoding.UTF8, false, 1024, true)) b.Read(reader, 10, false); - Assert.AreEqual(a, b); + Assert.AreEqual(a.ToString(10), b.ToString(10)); Assert.AreEqual(ms.Length, ms.Position); Assert.AreEqual((char)0xFEFF + a.ToString(10), Encoding.UTF8.GetString(ms.ToArray())); } @@ -137,7 +137,7 @@ namespace MPIR.Tests.HugeFloatTests using(var reader = new StreamReader(ms, Encoding.UTF8, false, 1024, true)) b.Read(reader, 62, false); - Assert.AreEqual(a, b); + Assert.AreEqual(a.ToString(62), b.ToString(62)); Assert.AreEqual(ms.Length, ms.Position); Assert.AreEqual((char)0xFEFF + a.ToString(62), Encoding.UTF8.GetString(ms.ToArray())); } diff --git a/mpir.net/mpir.net/HugeFloat.cpp b/mpir.net/mpir.net/HugeFloat.cpp index 24a30cef..4fcb2828 100644 --- a/mpir.net/mpir.net/HugeFloat.cpp +++ b/mpir.net/mpir.net/HugeFloat.cpp @@ -71,14 +71,15 @@ namespace MPIR return result; } - void MPTYPE::FromString(String^ value, int base) + void MPTYPE::FromString(String^ value, int base, bool exponentInDecimal) { AllocateStruct(); MP(init)(_value); _allocatedPrecision = MP(get_prec)(_value); + base = Math::Abs(base); IntPtr ptr = Marshal::StringToHGlobalAnsi(value); - bool success = 0 == MP(set_str)(_value, (char*)(void*)ptr, base); + bool success = 0 == MP(set_str)(_value, (char*)(void*)ptr, exponentInDecimal ? -base : base); Marshal::FreeHGlobal(ptr); if(!success) @@ -88,10 +89,11 @@ namespace MPIR } } - void MPTYPE::SetTo(String^ value, int base) + void MPTYPE::SetTo(String^ value, int base, bool exponentInDecimal) { + base = Math::Abs(base); IntPtr ptr = Marshal::StringToHGlobalAnsi(value); - bool success = 0 == MP(set_str)(_value, (char*)(void*)ptr, base); + bool success = 0 == MP(set_str)(_value, (char*)(void*)ptr, exponentInDecimal ? -base : base); Marshal::FreeHGlobal(ptr); if(!success) @@ -339,7 +341,82 @@ namespace MPIR size_t MPTYPE::Read(TextReader^ reader, int base, bool exponentInDecimal) { - throw gcnew NotImplementedException(); + StringBuilder str; + + int c; + size_t nread = 0; + const unsigned char* digit_value = __gmp_digit_value_tab; + bool hasDecimal = false; + bool inExponent = false; + + if (base < 2) + throw gcnew ArgumentException("Invalid base", "base"); + + if (base > 36) + { + // For bases > 36, use the collating sequence + // 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz + digit_value += 224; + if (base > 62) + throw gcnew ArgumentException("Invalid base", "base"); + } + + // Skip whitespace + while ((c = reader->Peek()) >= 0 && Char::IsWhiteSpace(c)) + { + nread++; + reader->Read(); + } + + // possibly negative + if (c == '-') + { + str.Append((wchar_t)c); + PEEK_NEXT_CHAR; + } + + while (c != EOF) + { + if (c == '.') + { + if (hasDecimal) break; + + hasDecimal = true; + str.Append((wchar_t)c); + PEEK_NEXT_CHAR; + continue; + } + + if ((base <= 10 && Char::ToLower(c) == 'e') || c == '@') + { + if (inExponent) break; + + inExponent = true; + str.Append((wchar_t)c); + PEEK_NEXT_CHAR; + + // possibly negative + if (c == '-') + { + str.Append((wchar_t)c); + PEEK_NEXT_CHAR; + } + continue; + } + + if (inExponent && exponentInDecimal && !Char::IsDigit(c)) + break; + + int dig = digit_value[c]; + if (dig >= base) + break; + + str.Append((wchar_t)c); + PEEK_NEXT_CHAR; + } + + SetTo(str.ToString(), base, exponentInDecimal); + return str.Length + nread; } #pragma endregion diff --git a/mpir.net/mpir.net/HugeFloat.h b/mpir.net/mpir.net/HugeFloat.h index d4878917..b1832142 100644 --- a/mpir.net/mpir.net/HugeFloat.h +++ b/mpir.net/mpir.net/HugeFloat.h @@ -761,7 +761,7 @@ namespace MPIR { _value = (MP(ptr))((*__gmp_allocate_func)(sizeof(MPSTRUCT))); } - void FromString(String^ value, int base); + void FromString(String^ value, int base, bool exponentInDecimal); MPTYPE(bool initialize); String^ ToString(int base, bool lowercase, int maxDigits, bool exponentInDecimal); @@ -820,21 +820,33 @@ namespace MPIR static MPTYPE^ Allocate(mp_bitcnt_t precision); /// - /// Initializes a new float instance and sets its value from the specified string, using leading characters to recognize the base: - /// 0x and 0X for hexadecimal, 0b and 0B for binary, 0 for octal, or decimal otherwise. + /// Initializes a new float instance and sets its value from the specified string. + /// No leading base characters are allowed. + /// The exponent is always in decimal. /// /// string representing the initial value for the new instance. Whitespace in the string is ignored. - MPTYPE(String^ value) { FromString(value, 0); } + MPTYPE(String^ value) { FromString(value, 0, true); } /// /// Initializes a new float instance and sets its value from the specified string /// /// string representing the initial value for the new instance. Whitespace in the string is ignored. /// base the string is in. - /// The base may vary from 2 to 62, or if base is 0, then the leading characters are used: 0x and 0X for hexadecimal, 0b and 0B for binary, 0 for octal, or decimal otherwise. + /// The base may vary from 2 to 62. No leading base characters are allowed. The exponent is always in decimal /// For bases up to 36, case is ignored; upper-case and lower-case letters have the same value. /// For bases 37 to 62, upper-case letter represent the usual 10..35 while lower-case letter represent 36..61. - MPTYPE(String^ value, int base) { FromString(value, base); } + MPTYPE(String^ value, int base) { FromString(value, base, true); } + + /// + /// Initializes a new float instance and sets its value from the specified string + /// + /// string representing the initial value for the new instance. Whitespace in the string is ignored. + /// base the string is in. + /// The base may vary from 2 to 62. No leading base characters are allowed. + /// For bases up to 36, case is ignored; upper-case and lower-case letters have the same value. + /// For bases 37 to 62, upper-case letter represent the usual 10..35 while lower-case letter represent 36..61. + /// True if the exponent is in decimal, false to use the same base as the mantissa. + MPTYPE(String^ value, int base, bool exponentInDecimal) { FromString(value, base, exponentInDecimal); } /// /// Initializes a new float instance and sets its value to the result of computing the source expression. @@ -1049,11 +1061,24 @@ namespace MPIR /// If the fraction is not in canonical form, Canonicalize() must be called. /// /// new value for the object - /// base the string is in. - /// The base may vary from 2 to 62, or if base is 0, then the leading characters are used: 0x and 0X for hexadecimal, 0b and 0B for binary, 0 for octal, or decimal otherwise. + /// base the mantissa in the string is in. Exponent is always in decimcal. + /// The base may vary from 2 to 62. No leading leading base characters are allowed. /// For bases up to 36, case is ignored; upper-case and lower-case letters have the same value. /// For bases 37 to 62, upper-case letter represent the usual 10..35 while lower-case letter represent 36..61. - void SetTo(String^ value, int base); + void SetTo(String^ value, int base) { SetTo(value, base, true); } + + /// + /// Sets the value of the float object. + /// Do not change the value of an object while it is contained in a hash table, because that changes its hash code. + /// If the fraction is not in canonical form, Canonicalize() must be called. + /// + /// new value for the object + /// base the string is in. + /// The base may vary from 2 to 62. No leading leading base characters are allowed. + /// For bases up to 36, case is ignored; upper-case and lower-case letters have the same value. + /// For bases 37 to 62, upper-case letter represent the usual 10..35 while lower-case letter represent 36..61. + /// If true, the exponent is in decimal; otherwise it is in the same base as the mantissa + void SetTo(String^ value, int base, bool exponentInDecimal); /// /// Sets the value of the float object.