From 5ef1d11323fc9036e9c281ffb056a005248c3a0f Mon Sep 17 00:00:00 2001 From: Alex Dyachenko Date: Thu, 21 May 2015 21:39:47 -0400 Subject: [PATCH] Float size checks and ToDouble(out exp). Apparently duplicate work, committing before merging from remote. --- .../HugeFloatTests/Conversions.cs | 203 ++++++++++++++++++ mpir.net/mpir.net/HugeFloat.h | 103 +++++---- mpir.net/mpir.net/HugeInt.h | 2 +- 3 files changed, 267 insertions(+), 41 deletions(-) diff --git a/mpir.net/mpir.net-tests/HugeFloatTests/Conversions.cs b/mpir.net/mpir.net-tests/HugeFloatTests/Conversions.cs index 45ec6fa8..21006d0e 100644 --- a/mpir.net/mpir.net-tests/HugeFloatTests/Conversions.cs +++ b/mpir.net/mpir.net-tests/HugeFloatTests/Conversions.cs @@ -83,6 +83,22 @@ namespace MPIR.Tests.HugeFloatTests } } + [TestMethod] + public void FloatToAndFromDouble2() + { + using (var a = new HugeFloat()) + { + a.SetTo(-123.25); + + long exp; + double c = a.ToDouble(out exp); + Assert.IsTrue(c.Equals(-0.962890625)); + Assert.AreEqual(7L, exp); + Assert.IsTrue(a.Equals(-123.25)); + Assert.AreEqual("-0.12325@3", a.ToString()); + } + } + [TestMethod] public void FloatToAndFromFloat() { @@ -97,6 +113,73 @@ namespace MPIR.Tests.HugeFloatTests } } + [TestMethod] + public void FloatToAndFromUlong() + { + using (var a = new HugeFloat()) + { + ulong b = 0xF84739ABCDEF4876; + a.SetTo(b); + + a.Value = -a; + ulong c = a.ToUlong(); + Assert.AreEqual(b, c); + } + } + + [TestMethod] + public void FloatToAndFromLong() + { + using (var a = new HugeFloat()) + { + long b = -0x784739ABCDEF4876; + a.SetTo(b); + + long c = a.ToLong(); + Assert.AreEqual(b, c); + } + } + + [TestMethod] + public void FloatToUlong2() + { + using (var a = new HugeFloat()) + using (var small = new HugeFloat(0.0001)) + { + ulong b = ulong.MaxValue; + a.SetTo(b); + a.Value = a + 1 - small; + var c = a.ToUlong(); + + Assert.AreEqual(b, c); + + a.Value = -1 + small; + c = a.ToUlong(); + Assert.AreEqual(0UL, c); + } + } + + [TestMethod] + public void FloatToLong2() + { + using (var a = new HugeFloat()) + using (var small = new HugeFloat(0.0001)) + { + long b = long.MaxValue; + a.SetTo(b); + a.Value = a + 1 - small; + var c = a.ToLong(); + + Assert.AreEqual(b, c); + + b = long.MinValue; + a.SetTo(b); + a.Value -= 1 - small; + c = a.ToLong(); + Assert.AreEqual(b, c); + } + } + [TestMethod] public void FloatFromString() { @@ -136,6 +219,126 @@ namespace MPIR.Tests.HugeFloatTests } } + [TestMethod] + public void FloatFitsUlong() + { + using (var a = new HugeFloat(ulong.MaxValue)) + using (var small = new HugeFloat(0.0001)) + { + Assert.IsTrue(a.FitsUlong()); + a.Value = a + 1; + Assert.IsFalse(a.FitsUlong()); + a.Value = a - small; + Assert.IsTrue(a.FitsUlong()); + a.SetTo(0); + Assert.IsTrue(a.FitsUlong()); + a.Value = a - 1; + Assert.IsFalse(a.FitsUlong()); + a.Value = a + small; + Assert.IsTrue(a.FitsUlong()); + } + } + + [TestMethod] + public void FloatFitsLong() + { + using (var a = new HugeFloat(long.MaxValue)) + using (var small = new HugeFloat(0.0001)) + { + Assert.IsTrue(a.FitsLong()); + a.Value = a + 1; + Assert.IsFalse(a.FitsLong()); + a.Value = a - small; + Assert.IsTrue(a.FitsLong()); + a.SetTo(long.MinValue); + Assert.IsTrue(a.FitsLong()); + a.Value = a - 1; + Assert.IsFalse(a.FitsLong()); + a.Value = a + small; + Assert.IsTrue(a.FitsLong()); + } + } + + [TestMethod] + public void FloatFitsUint() + { + using (var a = new HugeFloat(uint.MaxValue)) + using (var small = new HugeFloat(0.0001)) + { + Assert.IsTrue(a.FitsUint()); + a.Value = a + 1; + Assert.IsFalse(a.FitsUint()); + a.Value = a - small; + Assert.IsTrue(a.FitsUint()); + a.SetTo(0); + Assert.IsTrue(a.FitsUint()); + a.Value = a - 1; + Assert.IsFalse(a.FitsUint()); + a.Value = a + small; + Assert.IsTrue(a.FitsUint()); + } + } + + [TestMethod] + public void FloatFitsInt() + { + using (var a = new HugeFloat(int.MaxValue)) + using (var small = new HugeFloat(0.0001)) + { + Assert.IsTrue(a.FitsInt()); + a.Value = a + 1; + Assert.IsFalse(a.FitsInt()); + a.Value = a - small; + Assert.IsTrue(a.FitsInt()); + a.SetTo(int.MinValue); + Assert.IsTrue(a.FitsInt()); + a.Value = a - 1; + Assert.IsFalse(a.FitsInt()); + a.Value = a + small; + Assert.IsTrue(a.FitsInt()); + } + } + + [TestMethod] + public void FloatFitsUshort() + { + using (var a = new HugeFloat(ushort.MaxValue)) + using (var small = new HugeFloat(0.0001)) + { + Assert.IsTrue(a.FitsUshort()); + a.Value = a + 1; + Assert.IsFalse(a.FitsUshort()); + a.Value = a - small; + Assert.IsTrue(a.FitsUshort()); + a.SetTo(0); + Assert.IsTrue(a.FitsUshort()); + a.Value = a - 1; + Assert.IsFalse(a.FitsUshort()); + a.Value = a + small; + Assert.IsTrue(a.FitsUshort()); + } + } + + [TestMethod] + public void FloatFitsShort() + { + using (var a = new HugeFloat(short.MaxValue)) + using (var small = new HugeFloat(0.0001)) + { + Assert.IsTrue(a.FitsShort()); + a.Value = a + 1; + Assert.IsFalse(a.FitsShort()); + a.Value = a - small; + Assert.IsTrue(a.FitsShort()); + a.SetTo(short.MinValue); + Assert.IsTrue(a.FitsShort()); + a.Value = a - 1; + Assert.IsFalse(a.FitsShort()); + a.Value = a + small; + Assert.IsTrue(a.FitsShort()); + } + } + //private void AssertBetween(int min, int max, long actual) //{ // Assert.IsTrue(actual >= min && actual <= max, "Expected {0} to {1}, actual {2}", min, max, actual); diff --git a/mpir.net/mpir.net/HugeFloat.h b/mpir.net/mpir.net/HugeFloat.h index 2c40e2c4..9e18af18 100644 --- a/mpir.net/mpir.net/HugeFloat.h +++ b/mpir.net/mpir.net/HugeFloat.h @@ -891,21 +891,21 @@ namespace MPIR /// A string representation of the number in the specified base. String^ ToString(int base, bool lowercase) { return ToString(base, lowercase, 0); } -///// -///// Returns the absolute value of the number as a ulong. -///// If the number is too big, then just the least significant bits that do fit are returned. -///// The sign of the number is ignored, only the absolute value is used. -///// -///// The absolute value as a ulong, possibly truncated to the least significant bits only. -//mpir_ui ToUlong() { return MP(get_ui)(_value); } + /// + /// Returns the absolute value of the number as a ulong, truncating any fractional part. + /// If the number is too big, the result is undefined. Call FitsUlong() to check if the number will fit. + /// The sign of the number is ignored, only the absolute value is used. + /// + /// The absolute value as a ulong, with any fractional part truncated. + mpir_ui ToUlong() { return MP(get_ui)(_value); } -///// -///// Returns the value of the number as a long. -///// If the number is too big, then just the least significant bits that do fit are returned, with the same sign as the number. -///// When truncation occurs, the result is propobly not very useful. Call FitsLong() to check if the number will fit. -///// -///// The value as a ulong, possibly truncated to the least significant bits only. -//mpir_si ToLong() { return MP(get_si)(_value); } + /// + /// Returns the value of the number as a long. + /// If the number is too big, the result is undefined. Call FitsLong() to check if the number will fit. + /// + /// + /// The value as a long, possibly truncated to the least significant bits only. + mpir_si ToLong() { return MP(get_si)(_value); } /// /// Returns the value of the number as a double, truncating if necessary (rounding towards zero). @@ -914,19 +914,20 @@ namespace MPIR /// The value as a double, possibly truncated. double ToDouble() { return MP(get_d)(_value); } -///// -///// Returns the value of the number as a double, truncating if necessary (rounding towards zero), and returning the exponent separately. -///// The return is the mantissa, its absolute value will be in the range [0.5 - 1). ///// If the source value is zero, both mantissa and exponent are returned as 0. -///// -///// variable to store the exponent in. -///// The mantissa of the value as a double, possibly truncated. -//double ToDouble([Out] mpir_si% exp) -//{ -// mpir_si x; -// auto result = MP(get_d_2exp)(&x, _value); -// exp = x; -// return result; -//} + /// + /// Returns the value of the number as a double, truncating if necessary (rounding towards zero), and returning the exponent separately. + /// The return is the mantissa, its absolute value will be in the range [0.5 - 1). /// The exponent is binary, i.e. mantissa * 2^exp is the value of the source number. + /// If the source value is zero, both mantissa and exponent are returned as 0. + /// + /// variable to store the exponent in. + /// The mantissa of the value as a double, possibly truncated. + double ToDouble([Out] mpir_si% exp) + { + mpir_si x; + auto result = MP(get_d_2exp)(&x, _value); + exp = x; + return result; + } #pragma endregion @@ -1045,19 +1046,41 @@ namespace MPIR #pragma region Size checks -///// -///// Returns the number of digits the number would take if written in the specified base. -///// The sign of the number is ignored, just the absolute value is used. -///// The result will be either exact or at most 2 characters too big. -///// If is a power of 2, the result will always be exact. -///// If the number is 0, the result is always 3. -///// This function can be used to estimate the space required when converting to a string. -///// The right amount of allocation is normally two more than the value returned, -///// one extra for a minus sign and one for the null-terminator. -///// A slash between numerator and denominator is accounted for. -///// Numeric base for the would-be string conversion, in the range from 2 to 62. -///// The number of digits the number would take written in the specified base, possibly 1 or 2 too big, not counting a leading minus. -//mp_size_t ApproximateSizeInBase(int base) { return mpz_sizeinbase(&_value->_mp_num, base) + mpz_sizeinbase(&_value->_mp_den, base) + 1; } + /// + /// Returns true if the value of the float, when truncated to an integer, is in the ulong range. + /// + /// true if the value will fit in a ulong + bool FitsUlong() { return MP(fits_ui_p)(_value) != 0; } + + /// + /// Returns true if the value of the float, when truncated to an integer, is in the long range. + /// + /// true if the value will fit in a long + bool FitsLong() { return MP(fits_si_p)(_value) != 0; } + + /// + /// Returns true if the value of the float, when truncated to an integer, is in the uint range. + /// + /// true if the value will fit in a uint + bool FitsUint() { return MP(fits_uint_p)(_value) != 0; } + + /// + /// Returns true if the value of the float, when truncated to an integer, is in the int range. + /// + /// true if the value will fit in a int + bool FitsInt() { return MP(fits_sint_p)(_value) != 0; } + + /// + /// Returns true if the value of the float, when truncated to an integer, is in the ushort range. + /// + /// true if the value will fit in a ushort + bool FitsUshort() { return MP(fits_ushort_p)(_value) != 0; } + + /// + /// Returns true if the value of the float, when truncated to an integer, is in the short range. + /// + /// true if the value will fit in a short + bool FitsShort() { return MP(fits_sshort_p)(_value) != 0; } #pragma endregion diff --git a/mpir.net/mpir.net/HugeInt.h b/mpir.net/mpir.net/HugeInt.h index bab4cb8b..f54d9f9b 100644 --- a/mpir.net/mpir.net/HugeInt.h +++ b/mpir.net/mpir.net/HugeInt.h @@ -1541,7 +1541,7 @@ namespace MPIR /// If the number is too big, then just the least significant bits that do fit are returned, with the same sign as the number. /// When truncation occurs, the result is propobly not very useful. Call FitsLong() to check if the number will fit. /// - /// The value as a ulong, possibly truncated to the least significant bits only. + /// The value as a long, possibly truncated to the least significant bits only. mpir_si ToLong() { return MP(get_si)(_value); } ///