diff --git a/IronPython.slnx b/IronPython.slnx
index b2e45da30..e8a180378 100644
--- a/IronPython.slnx
+++ b/IronPython.slnx
@@ -65,5 +65,6 @@
+
diff --git a/src/core/IronPython.StdLib b/src/core/IronPython.StdLib
index 6e08c8264..ffa4f648d 160000
--- a/src/core/IronPython.StdLib
+++ b/src/core/IronPython.StdLib
@@ -1 +1 @@
-Subproject commit 6e08c8264990fc712d122a7e8e44bd11dd964b5f
+Subproject commit ffa4f648d640f6ea99218bbb8aceaa208a4fa84f
diff --git a/src/core/IronPython/IronPython.csproj b/src/core/IronPython/IronPython.csproj
index 79cb7650b..a8dcf92ed 100644
--- a/src/core/IronPython/IronPython.csproj
+++ b/src/core/IronPython/IronPython.csproj
@@ -83,6 +83,13 @@
+
+
+ false
+ Analyzer
+
+
+
diff --git a/src/core/IronPython/Modules/sys.cs b/src/core/IronPython/Modules/sys.cs
index aab7ef41d..40f542f20 100644
--- a/src/core/IronPython/Modules/sys.cs
+++ b/src/core/IronPython/Modules/sys.cs
@@ -119,7 +119,7 @@ public static void setcheckinterval(int value) {
throw PythonOps.NotImplementedError("IronPython does not support sys.setcheckinterval");
}
- // warnoptions is set by PythonContext and updated on each reload
+ // warnoptions is set by PythonContext and updated on each reload
public static PythonTuple exc_info(CodeContext/*!*/ context) {
return PythonOps.GetExceptionInfo(context);
@@ -294,7 +294,7 @@ public override string __repr__(CodeContext context) {
#if PROFILE_SUPPORT
// not enabled because we don't yet support tracing built-in functions. Doing so is a little
- // difficult because it's hard to flip tracing on/off for them w/o a perf overhead in the
+ // difficult because it's hard to flip tracing on/off for them w/o a perf overhead in the
// non-profiling case.
public static void setprofile(CodeContext/*!*/ context, TracebackDelegate o) {
PythonContext pyContext = context.LanguageContext;
@@ -489,11 +489,7 @@ public override string __repr__(CodeContext context) {
}
}
-#if NETCOREAPP3_1_OR_GREATER
public static string float_repr_style = "short";
-#else
- public static string float_repr_style = "legacy";
-#endif
public static floatinfo float_info = new floatinfo(
Double.MaxValue, // DBL_MAX
@@ -603,7 +599,7 @@ public static void PerformModuleReload(PythonContext/*!*/ context, PythonDiction
dict["platform"] = PythonContext.GetPlatform();
- // !!! These fields do need to be reset on "reload(sys)". However, the initial value is specified by the
+ // !!! These fields do need to be reset on "reload(sys)". However, the initial value is specified by the
// engine elsewhere. For now, we initialize them just once to some default value
dict["warnoptions"] = new PythonList(0);
diff --git a/src/core/IronPython/Runtime/Operations/FloatOps.cs b/src/core/IronPython/Runtime/Operations/FloatOps.cs
index 4424be6e0..3e722e95e 100644
--- a/src/core/IronPython/Runtime/Operations/FloatOps.cs
+++ b/src/core/IronPython/Runtime/Operations/FloatOps.cs
@@ -733,27 +733,8 @@ public static bool NotEquals(Double x, int y) {
return x != y;
}
- internal static string Repr(CodeContext/*!*/ context, double self, bool trailingZeroAfterWholeFloat) {
- if (double.IsNaN(self)) {
- return "nan";
- }
-
- // first format using Python's specific formatting rules...
- string res = StringFormatter.Format(context, "%.17g", self, trailingZeroAfterWholeFloat: trailingZeroAfterWholeFloat);
- if (LiteralParser.ParseFloat(res) == self) {
- return res;
- }
-#if NETCOREAPP3_1_OR_GREATER
- Debug.Assert(false);
-#endif
-
- // if it's not round trippable though use .NET's round-trip format
- res = self.ToString("R", CultureInfo.InvariantCulture);
- if (trailingZeroAfterWholeFloat && !res.Contains('.')) {
- res += ".0";
- }
- return res;
- }
+ internal static string Repr(CodeContext/*!*/ context, double self, bool trailingZeroAfterWholeFloat)
+ => Schubfach.dtoa(self, trailingZeroAfterWholeFloat);
public static string __repr__(CodeContext/*!*/ context, double self)
=> Repr(context, self, trailingZeroAfterWholeFloat: true);
diff --git a/src/core/IronPython/Runtime/Schubfach.cs b/src/core/IronPython/Runtime/Schubfach.cs
new file mode 100644
index 000000000..7250fc7e3
--- /dev/null
+++ b/src/core/IronPython/Runtime/Schubfach.cs
@@ -0,0 +1,308 @@
+// Implementation of the Schubfach algorithm:
+// https://fmt.dev/papers/Schubfach4.pdf.
+// Copyright (c) 2025 Victor Zverovich
+// Distributed under the MIT license.
+// Ported from schubfach.cc of https://github.com/vitaut/schubfach.
+
+#nullable enable
+
+using System;
+
+namespace IronPython.Runtime {
+ internal static partial class Schubfach {
+ private const int buffer_size = 24; // schubfach.cc is 25, but we don't need the extra byte for null-terminator.
+
+ // pow10_significands is generated by IronPython.SourceGenerator.SchubfachGenerator.
+
+ // Computes 128-bit result of multiplication of two 64-bit unsigned integers.
+ private static ulong umul128(ulong x, ulong y, out ulong lo) {
+#if NET
+ return Math.BigMul(x, y, out lo);
+#else
+ const ulong mask = ~0u;
+
+ ulong a = x >> 32;
+ ulong b = x & mask;
+ ulong c = y >> 32;
+ ulong d = y & mask;
+
+ ulong ac = a * c;
+ ulong bc = b * c;
+ ulong ad = a * d;
+ ulong bd = b * d;
+
+ ulong intermediate = (bd >> 32) + (ad & mask) + (bc & mask);
+
+ lo = (intermediate << 32) + (bd & mask);
+ return ac + (intermediate >> 32) + (ad >> 32) + (bc >> 32);
+#endif
+ }
+
+ // Computes upper 64 bits of multiplication of pow10 and scaled_sig with
+ // modified round-to-odd rounding of the result,
+ // where pow10 = (pow10_hi << 63) | pow10_lo.
+ private static ulong umul192_upper64_modified(ulong pow10_hi, ulong pow10_lo, ulong scaled_sig) {
+ ulong x_hi = umul128(pow10_lo, scaled_sig, out _);
+ ulong y_hi = umul128(pow10_hi, scaled_sig, out ulong y_lo);
+ ulong z = (y_lo >> 1) + x_hi;
+ ulong result = y_hi + (z >> 63);
+ const ulong mask = ((ulong)1 << 63) - 1;
+ // OR with 1 if z is not divisible by 2**63.
+ return result | (((z & mask) + mask) >> 63);
+ }
+
+ // log10_2_sig = round(log10(2) * 2**log10_2_exp)
+ private const long log10_2_sig = 661_971_961_083;
+ private const int log10_2_exp = 41;
+
+ // Computes floor(log10(pow(2, e))) for e <= 5456721.
+ private static int floor_log10_pow2(int e) {
+ return (int)(e * log10_2_sig >> log10_2_exp);
+ }
+
+ private static void write8digits(Span buffer, ref int pos, uint n) {
+ // Based on Division-Free Binary-to-Decimal Conversion:
+ // https://inria.hal.science/hal-00864293/.
+ const int shift = 28;
+ const ulong magic = 193_428_131_138_340_668;
+ uint y = (uint)((umul128(((ulong)n + 1) << shift, magic, out _) >> 20) - 1);
+ for (int i = 0; i < 8; ++i) {
+ uint t = 10 * y;
+ buffer[pos++] = (char)('0' + (t >> shift));
+ y = t & ((1u << shift) - 1);
+ }
+ }
+
+ private static readonly ulong[] pow10 = [
+ 1,
+ 10,
+ 100,
+ 1_000,
+ 10_000,
+ 100_000,
+ 1_000_000,
+ 10_000_000,
+ 100_000_000,
+ 1_000_000_000,
+ 10_000_000_000,
+ 100_000_000_000,
+ 1_000_000_000_000,
+ 10_000_000_000_000,
+ 100_000_000_000_000,
+ 1_000_000_000_000_000,
+ 10_000_000_000_000_000,
+ 100_000_000_000_000_000,
+ ];
+
+ // Rewrites the digits in buffer[start..pos] in fixed notation if Python's
+ // repr would, i.e. for -4 < dec_point <= 16.
+ private static bool try_write_fixed(Span buffer, ref int pos, int start, int dec_exp, bool add_dot_0) {
+ int num_digits = pos - start;
+ int dec_point = dec_exp + 1;
+ if (dec_point <= -4 || dec_point > 16) return false;
+
+ if (dec_point <= 0) {
+ // 0.000ddd
+ int prefix = 2 - dec_point;
+ buffer.Slice(start, num_digits).CopyTo(buffer.Slice(start + prefix));
+ buffer[start] = '0';
+ buffer[start + 1] = '.';
+ buffer.Slice(start + 2, -dec_point).Fill('0');
+ pos += prefix;
+ } else if (dec_point >= num_digits) {
+ // ddd000.0
+ buffer.Slice(pos, dec_point - num_digits).Fill('0');
+ pos += dec_point - num_digits;
+ if (add_dot_0) {
+ buffer[pos++] = '.';
+ buffer[pos++] = '0';
+ }
+ } else {
+ // ddd.ddd
+ buffer.Slice(start + dec_point, num_digits - dec_point).CopyTo(buffer.Slice(start + dec_point + 1));
+ buffer[start + dec_point] = '.';
+ ++pos;
+ }
+ return true;
+ }
+
+ // Writes the decimal FP number dec_sig * 10**dec_exp to buffer.
+ private static string write(bool is_negative, ulong dec_sig, int dec_exp, bool add_dot_0) {
+ Span buffer = stackalloc char[buffer_size];
+ int pos = 0;
+ if (is_negative) buffer[pos++] = '-';
+
+ int len = floor_log10_pow2(64 - countl_zero(dec_sig)); // 64 = std::numeric_limits::digits
+ if (dec_sig >= pow10[len]) ++len;
+ dec_sig *= pow10[17 - len]; // 17 = std::numeric_limits::max_digits10
+ dec_exp += len - 1;
+
+ const int pow10_8 = 100_000_000;
+ int start = pos;
+ uint hi = (uint)(dec_sig / pow10_8);
+ buffer[pos++] = (char)('0' + hi / pow10_8);
+ // schubfach.cc adds the . here; we add it later so try_write_fixed can place it.
+ write8digits(buffer, ref pos, hi % pow10_8);
+ uint lo = (uint)(dec_sig % pow10_8);
+ if (lo != 0) write8digits(buffer, ref pos, lo);
+
+ // Remove trailing zeros.
+ while (buffer[pos - 1] == '0') --pos;
+
+ if (try_write_fixed(buffer, ref pos, start, dec_exp, add_dot_0)) {
+ return buffer.Slice(0, pos).ToString();
+ }
+
+ // Fallback to scientific notation if fixed notation is not used.
+ int num_digits = pos - start;
+ if (num_digits > 1) {
+ buffer.Slice(start + 1, num_digits - 1).CopyTo(buffer.Slice(start + 2));
+ buffer[start + 1] = '.';
+ ++pos;
+ }
+
+ buffer[pos++] = 'e';
+ char sign = '+';
+ if (dec_exp < 0) {
+ sign = '-';
+ dec_exp = -dec_exp;
+ }
+ buffer[pos++] = sign;
+ if (dec_exp >= 100) {
+ buffer[pos++] = (char)('0' + dec_exp / 100);
+ dec_exp %= 100;
+ }
+ buffer[pos++] = (char)('0' + dec_exp / 10);
+ buffer[pos++] = (char)('0' + dec_exp % 10);
+ return buffer.Slice(0, pos).ToString();
+ }
+
+ /// Returns the shortest decimal representation of `value`, formatted like Python's repr.
+ public static string dtoa(double value, bool add_dot_0 = true) {
+ long bitsLong = BitConverter.DoubleToInt64Bits(value);
+ bool is_negative = bitsLong < 0;
+ ulong bits = unchecked((ulong)bitsLong);
+
+ const int num_sig_bits = 53 - 1; // 53 = std::numeric_limits::digits
+ const int exp_mask = 0x7ff;
+ int bin_exp = (int)(bits >> num_sig_bits) & exp_mask;
+
+ const ulong implicit_bit = (ulong)1 << num_sig_bits;
+ ulong bin_sig = bits & (implicit_bit - 1); // binary significand
+
+ bool regular = bin_sig != 0;
+ if (((bin_exp + 1) & exp_mask) <= 1) {
+ if (bin_exp != 0) return bin_sig != 0 ? "nan" : is_negative ? "-inf" : "inf";
+ if (bin_sig == 0) return is_negative ? (add_dot_0 ? "-0.0" : "-0") : (add_dot_0 ? "0.0" : "0");
+ // Handle subnormals.
+ bin_sig ^= implicit_bit;
+ ++bin_exp;
+ regular = true;
+ }
+ bin_sig ^= implicit_bit;
+ bin_exp -= num_sig_bits + 1023; // Remove the exponent bias.
+
+ // Handle small integers.
+ if ((bin_exp < 0) & (bin_exp >= -num_sig_bits)) {
+ ulong f = bin_sig >> -bin_exp;
+ if (f << -bin_exp == bin_sig) {
+ return write(is_negative, f, 0, add_dot_0);
+ }
+ }
+
+ // Shift the significand so that boundaries are integer.
+ ulong bin_sig_shifted = bin_sig << 2;
+
+ // Compute the shifted boundaries of the rounding interval (Rv).
+ ulong lower = bin_sig_shifted - (regular ? 2u : 1u);
+ ulong upper = bin_sig_shifted + 2;
+
+ // log10_3_over_4_sig = round(log10(3/4) * 2**log10_2_exp)
+ const long log10_3_over_4_sig = -274_743_187_321;
+
+ // Compute the decimal exponent as floor(log10(2**bin_exp)) if regular or
+ // floor(log10(3/4 * 2**bin_exp)) otherwise, without branching.
+ int dec_exp = (int)((bin_exp * log10_2_sig + (!regular ? log10_3_over_4_sig : 0)) >> log10_2_exp);
+
+ const int dec_exp_min = -292;
+ int pow10_index = 2 * (-dec_exp - dec_exp_min);
+ ulong pow10_hi = pow10_significands[pow10_index], pow10_lo = pow10_significands[pow10_index + 1];
+
+ // round(log2(10) * 2**log2_pow10_exp) + 1
+ const int log2_pow10_sig = 217_707, log2_pow10_exp = 16;
+
+ // floor(log2(10**-dec_exp))
+ int pow10_bin_exp = -dec_exp * log2_pow10_sig >> log2_pow10_exp;
+ // pow10 = ((pow10_hi << 63) | pow10_lo) * 2**(pow10_bin_exp - 126 + 1)
+
+ // Shift to ensure the intermediate result in umul192_upper64_modified has
+ // a fixed 128-bit fractional width. For example, 3 * 2**59 and 3 * 2**60
+ // both have dec_exp = 2 and dividing them by 10**dec_exp would have the
+ // decimal point in different (bit) positions without the shift:
+ // 3 * 2**59 / 100 = 1.72...e+16 (shift = 3)
+ // 3 * 2**60 / 100 = 3.45...e+16 (shift = 4)
+ int shift = bin_exp + pow10_bin_exp + 2;
+
+ ulong scaled_sig = umul192_upper64_modified(pow10_hi, pow10_lo, bin_sig_shifted << shift);
+
+ // Compute the estimates of lower and upper bounds of the rounding interval
+ // by multiplying them by the power of 10 and applying modified rounding.
+ lower = umul192_upper64_modified(pow10_hi, pow10_lo, lower << shift);
+ upper = umul192_upper64_modified(pow10_hi, pow10_lo, upper << shift);
+
+ ulong dec_sig_under = scaled_sig >> 2;
+ ulong bin_sig_lsb = bin_sig & 1;
+ if (dec_sig_under >= 10) {
+ // Compute the significands of the under- and overestimate.
+ ulong dec_sig_under2 = 10 * (dec_sig_under / 10);
+ ulong dec_sig_over2 = dec_sig_under2 + 10;
+ // Check if the under- and overestimates are in the interval.
+ bool under_in2 = lower + bin_sig_lsb <= (dec_sig_under2 << 2);
+ bool over_in2 = (dec_sig_over2 << 2) + bin_sig_lsb <= upper;
+ if (under_in2 != over_in2) {
+ return write(is_negative, under_in2 ? dec_sig_under2 : dec_sig_over2, dec_exp, add_dot_0);
+ }
+ }
+
+ ulong dec_sig_over = dec_sig_under + 1;
+ bool under_in = lower + bin_sig_lsb <= (dec_sig_under << 2);
+ bool over_in = (dec_sig_over << 2) + bin_sig_lsb <= upper;
+ if (under_in != over_in) {
+ // Only one of dec_sig_under or dec_sig_over are in the rounding interval.
+ return write(is_negative, under_in ? dec_sig_under : dec_sig_over, dec_exp, add_dot_0);
+ }
+
+ // Both dec_sig_under and dec_sig_over are in the interval - pick the closest.
+ int cmp = unchecked((int)(scaled_sig - ((dec_sig_under + dec_sig_over) << 1)));
+ bool under_closer = cmp < 0 || cmp == 0 && (dec_sig_under & 1) == 0;
+ return write(is_negative, under_closer ? dec_sig_under : dec_sig_over, dec_exp, add_dot_0);
+ }
+
+ // std::countl_zero
+ private static int countl_zero(ulong x) {
+#if NET
+ return System.Numerics.BitOperations.LeadingZeroCount(x);
+#else
+ if (x == 0) return 64;
+ // fill trailing zeroes with ones
+ x |= x >> 1;
+ x |= x >> 2;
+ x |= x >> 4;
+ x |= x >> 8;
+ x |= x >> 16;
+ x |= x >> 32;
+ // deBruijn sequence, k=2, n=6 (2^6=64)
+ return 63 - debruijn[(int)(unchecked(x * 0x03f79d71b4cb0a89ul) >> 58)];
+#endif
+ }
+
+#if !NET
+ private static ReadOnlySpan debruijn => [
+ 0, 47, 1, 56, 48, 27, 2, 60, 57, 49, 41, 37, 28, 16, 3, 61,
+ 54, 58, 35, 52, 50, 42, 21, 44, 38, 32, 29, 23, 17, 11, 4, 62,
+ 46, 55, 26, 59, 40, 36, 15, 53, 34, 51, 20, 43, 31, 22, 10, 45,
+ 25, 39, 14, 33, 19, 30, 9, 24, 13, 18, 8, 12, 7, 6, 5, 63,
+ ];
+#endif
+ }
+}
diff --git a/src/roslyn/IronPython.Analyzer/IronPython.Analyzer.csproj b/src/roslyn/IronPython.Analyzer/IronPython.Analyzer.csproj
index 38c0895fe..75988068e 100644
--- a/src/roslyn/IronPython.Analyzer/IronPython.Analyzer.csproj
+++ b/src/roslyn/IronPython.Analyzer/IronPython.Analyzer.csproj
@@ -3,6 +3,7 @@
netstandard2.0
true
+ true
diff --git a/src/roslyn/IronPython.SourceGenerator/IronPython.SourceGenerator.csproj b/src/roslyn/IronPython.SourceGenerator/IronPython.SourceGenerator.csproj
new file mode 100644
index 000000000..fa20195c2
--- /dev/null
+++ b/src/roslyn/IronPython.SourceGenerator/IronPython.SourceGenerator.csproj
@@ -0,0 +1,14 @@
+
+
+
+ netstandard2.0
+ true
+ true
+
+
+
+
+
+
+
+
diff --git a/src/roslyn/IronPython.SourceGenerator/SchubfachGenerator.cs b/src/roslyn/IronPython.SourceGenerator/SchubfachGenerator.cs
new file mode 100644
index 000000000..6ed8adcc7
--- /dev/null
+++ b/src/roslyn/IronPython.SourceGenerator/SchubfachGenerator.cs
@@ -0,0 +1,59 @@
+// Power of 10 overestimates generator for the Schubfach algorithm:
+// https://fmt.dev/papers/Schubfach4.pdf.
+// Copyright (c) 2025 Victor Zverovich
+// Distributed under the MIT license.
+// Ported from gen-pow10.py of https://github.com/vitaut/schubfach.
+
+using System;
+using System.Numerics;
+using System.Text;
+
+using Microsoft.CodeAnalysis;
+
+namespace IronPython.SourceGenerator {
+ [Generator(LanguageNames.CSharp)]
+ public class SchubfachGenerator : IIncrementalGenerator {
+ public void Initialize(IncrementalGeneratorInitializationContext context) {
+ context.RegisterPostInitializationOutput(ctx => ctx.AddSource("Schubfach.pow10_significands.g.cs", Generate()));
+ }
+
+ private static string Generate() {
+ var sb = new StringBuilder();
+ sb.AppendLine("// ");
+ sb.AppendLine();
+ sb.AppendLine("namespace IronPython.Runtime {");
+ sb.AppendLine(" internal static partial class Schubfach {");
+ sb.AppendLine(" // 126-bit significands of overestimates of powers of 10.");
+ sb.AppendLine(" // Generated with IronPython.SourceGenerator.SchubfachGenerator.");
+ sb.AppendLine(" private static readonly ulong[] pow10_significands = [");
+
+ // Range of decimal exponents [K_min, K_max] from the paper.
+ const int dec_exp_min = -324;
+ const int dec_exp_max = 292;
+
+ const int num_bits = 126;
+
+ // Negate dec_pow_min and dec_pow_max because we need negative powers 10^-k.
+ for (int dec_exp = -dec_exp_max; dec_exp < -dec_exp_min + 1; dec_exp++) {
+ // dec_exp is -k in the paper.
+ int bin_exp = (int)Math.Floor(dec_exp * (Math.Log(10) / Math.Log(2))) - (num_bits - 1);
+ BigInteger bin_pow = BigInteger.Pow(2, Math.Abs(bin_exp));
+ BigInteger dec_pow = BigInteger.Pow(10, Math.Abs(dec_exp));
+ BigInteger result;
+ if (dec_exp < 0)
+ result = bin_pow / dec_pow;
+ else if (bin_exp < 0)
+ result = dec_pow * bin_pow;
+ else
+ result = dec_pow / bin_pow;
+ result = result + 1;
+ sb.AppendLine($" 0x{(ulong)(result >> 63):x}, 0x{(ulong)(result & (BigInteger.Pow(2, 63) - 1)):x16}, // {dec_exp,4}");
+ }
+
+ sb.AppendLine(" ];");
+ sb.AppendLine(" }");
+ sb.AppendLine("}");
+ return sb.ToString();
+ }
+ }
+}
diff --git a/tests/suite/modules/io_related/test_cPickle.py b/tests/suite/modules/io_related/test_cPickle.py
index da98a63ed..0dbe0d4fe 100644
--- a/tests/suite/modules/io_related/test_cPickle.py
+++ b/tests/suite/modules/io_related/test_cPickle.py
@@ -430,7 +430,7 @@ def normalize(test):
2:b'c%s\nNewClass_GetState_GetNewArgs\nq<\x00>X\x04\x00\x00\x00arg1q<\x01>K\x02K\x03\x87q<\x02>\x81q<\x03>X\x06\x00\x00\x00state1q<\x04>X\x06\x00\x00\x00state2q<\x05>\x86q<\x06>b.'.replace(b"%s", __name__.encode("ascii")),
}),
]
- if is_cli or sys.version_info < (3,5): #https://github.com/IronLanguages/main/issues/853
+ if not is_cli and sys.version_info < (3,5):
tests.append((0.33333333333333331,
{
0:b'F0.33333333333333331\n.',
diff --git a/tests/suite/modules/type_related/test_array.py b/tests/suite/modules/type_related/test_array.py
index 55fe4b111..c0ffcc5d6 100644
--- a/tests/suite/modules/type_related/test_array.py
+++ b/tests/suite/modules/type_related/test_array.py
@@ -481,12 +481,8 @@ def test_cp9348(self):
test_cases[('l', b"\x12\x34\x45\x67\x89\xab\xcd\xef")] = "array('l', [-1167088121787632622])"
test_cases[('L', b"\x12\x34\x45\x67\x89\xab\xcd\xef")] = "array('L', [17279655951921918994])"
- if is_cli: # https://github.com/IronLanguages/ironpython2/issues/102
- test_cases[('d', b"\x12\x34\x45\x67\x12\x34\x45\x67")] = "array('d', [2.9522485325887698e+189])"
- test_cases[('f', b"\x12\x34\x45\x67")] = "array('f', [9.3126672485384569e+23])"
- else:
- test_cases[('d', b"\x12\x34\x45\x67\x12\x34\x45\x67")] = "array('d', [2.95224853258877e+189])"
- test_cases[('f', b"\x12\x34\x45\x67")] = "array('f', [9.312667248538457e+23])"
+ test_cases[('d', b"\x12\x34\x45\x67\x12\x34\x45\x67")] = "array('d', [2.95224853258877e+189])"
+ test_cases[('f', b"\x12\x34\x45\x67")] = "array('f', [9.312667248538457e+23])"
for key in test_cases.keys():
type_code, param = key
diff --git a/tests/suite/stdlib/test_float.py b/tests/suite/stdlib/test_float.py
index 7a7834d84..e851387b3 100644
--- a/tests/suite/stdlib/test_float.py
+++ b/tests/suite/stdlib/test_float.py
@@ -8,7 +8,7 @@
import sys
-from iptest import is_ironpython, generate_suite, run_test
+from iptest import is_ironpython, generate_suite, run_test, is_netcoreapp
import test.test_float
@@ -18,14 +18,17 @@ def load_tests(loader, standard_tests, pattern):
if is_ironpython:
failing_tests = [
test.test_float.FormatTestCase('test_format'),
- test.test_float.FormatTestCase('test_format_testfile'),
test.test_float.FormatTestCase('test_issue5864'),
test.test_float.InfNanTest('test_nan_signs'),
- test.test_float.ReprTestCase('test_short_repr'),
test.test_float.RoundTestCase('test_large_n'),
test.test_float.RoundTestCase('test_matches_float_format'),
test.test_float.RoundTestCase('test_previous_round_bugs'),
]
+ if not is_netcoreapp:
+ failing_tests += [
+ test.test_float.FormatTestCase('test_format_testfile'),
+ test.test_float.ReprTestCase('test_short_repr'),
+ ]
if sys.version_info >= (3, 6):
failing_tests += [
test.test_float.GeneralFloatCases('test_underscores'), # https://github.com/IronLanguages/ironpython3/issues/105
diff --git a/tests/suite/test_float_repr.py b/tests/suite/test_float_repr.py
new file mode 100644
index 000000000..b632f2898
--- /dev/null
+++ b/tests/suite/test_float_repr.py
@@ -0,0 +1,158 @@
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the Apache 2.0 License.
+# See the LICENSE file in the project root for more information.
+
+"""
+Tests for float repr. The floats are built from their bits or from exact
+integers so these tests do not depend on the accuracy of decimal string parsing.
+"""
+
+import struct
+import unittest
+
+from iptest import run_test, skipUnlessIronPython
+
+def from_bits(bits):
+ return struct.unpack('> 63, g & ((1 << 63) - 1)]
+
+ self.assertEqual(list(table), expected)
+
+run_test(__name__)
diff --git a/tests/suite/test_formatting.py b/tests/suite/test_formatting.py
index 6ecce4e4c..dbabff9c7 100644
--- a/tests/suite/test_formatting.py
+++ b/tests/suite/test_formatting.py
@@ -393,21 +393,6 @@ def test_format_testfile(self):
"""the following is borrowed from stdlib"""
expected_failures = []
- if is_cli:
- expected_failures = """
-%r 0.03 -> 0.03
-%r 0.04 -> 0.04
-%r 0.05 -> 0.05
-%r 9999999999999999 -> 1e+16
-%r 1e16 -> 1e+16
-%r 1.001e-4 -> 0.0001001
-%r 1.00000000001e-4 -> 0.000100000000001
-%r 1.0000000001e-4 -> 0.00010000000001
-%r 0.9999999999999999e-4 -> 9.999999999999999e-05
-%r 0.999999999999e-4 -> 9.99999999999e-05
-%r 0.999e-4 -> 9.99e-05
-%r 1e-5 -> 1e-05
-""".strip().split("\n")
if not format_rounds_to_even:
expected_failures += """