From e60c691cb91addb8fcefa2f537e85ede6fb1e886 Mon Sep 17 00:00:00 2001
From: Simon Pichugin <simon.pichugin@gmail.com>
Date: Wed, 8 Jul 2026 17:32:09 -0700
Subject: [PATCH] Merge commit from fork

---
 pyasn1/type/univ.py             | 21 +++++++++----
 tests/codec/ber/test_decoder.py | 53 +++++++++++++++++++++++++++------
 tests/codec/cer/test_decoder.py | 10 +++++++
 tests/codec/der/test_decoder.py | 19 ++++++++++++
 tests/type/test_univ.py         | 40 +++++++++++++++++++++++++
 5 files changed, 129 insertions(+), 14 deletions(-)

Index: pyasn1-0.5.0/pyasn1/type/univ.py
===================================================================
--- pyasn1-0.5.0.orig/pyasn1/type/univ.py
+++ pyasn1-0.5.0/pyasn1/type/univ.py
@@ -1318,7 +1318,7 @@ class Real(base.SimpleAsn1Type):
     def __normalizeBase10(value):
         m, b, e = value
         while m and m % 10 == 0:
-            m /= 10
+            m //= 10
             e += 1
         return m, b, e
 
@@ -1457,10 +1457,21 @@ class Real(base.SimpleAsn1Type):
     def __float__(self):
         if self._value in self._inf:
             return self._value
-        else:
-            return float(
-                self._value[0] * pow(self._value[1], self._value[2])
-            )
+
+        mantissa, base, exponent = self._value
+
+        if not mantissa:
+            return 0.0
+
+        if base == 2:
+            return math.ldexp(float(mantissa), exponent)
+
+        # base is 10 (prettyIn() rejects everything else); refuse to
+        # materialize astronomically large integers via pow()
+        if exponent > sys.float_info.max_10_exp:
+            raise OverflowError('Real value too large to convert to float')
+
+        return float(mantissa * pow(base, exponent))
 
     def __abs__(self):
         return self.clone(abs(float(self)))
Index: pyasn1-0.5.0/tests/codec/ber/test_decoder.py
===================================================================
--- pyasn1-0.5.0.orig/tests/codec/ber/test_decoder.py
+++ pyasn1-0.5.0/tests/codec/ber/test_decoder.py
@@ -21,6 +21,7 @@ from pyasn1.type import univ
 from pyasn1.type import char
 from pyasn1.codec import streaming
 from pyasn1.codec.ber import decoder
+from pyasn1.codec.ber import encoder
 from pyasn1.codec.ber import eoo
 from pyasn1.compat.octets import ints2octs, str2octs, null
 from pyasn1 import error
@@ -487,17 +488,50 @@ class RealDecoderTestCase(BaseTestCase):
             ints2octs((9, 4, 161, 255, 1, 3))
         ) == (univ.Real((3, 2, -1020)), null)
 
-# TODO: this requires Real type comparison fix
+    def testBin6(self):  # large exponent, base = 16
+        value, rest = decoder.decode(
+            bytes((9, 5, 162, 0, 255, 255, 1))
+        )
+
+        assert tuple(value) == (1, 2, 262140)
+        assert rest == b''
+
+    def testBin7(self):  # large exponent in 4-octet form, base = 16
+        value, rest = decoder.decode(
+            bytes((9, 7, 227, 4, 1, 35, 69, 103, 1))
+        )
+
+        assert tuple(value) == (-1, 2, 76354972)
+        assert rest == b''
+
+    def testLargeBinaryRoundTrip(self):
+        substrate = encoder.encode(univ.Real((-1, 2, 76354972)))
+        value, rest = decoder.decode(substrate)
+
+        assert tuple(value) == (-1, 2, 76354972)
+        assert rest == b''
+
+    def testLongFormBinaryRealExponentLength(self):
+        value, rest = decoder.decode(
+            bytes((9, 6, 0x83, 3, 0x0f, 0x42, 0x40, 1))
+        )
+
+        assert tuple(value) == (1, 2, 1000000)
+        assert rest == b''
 
-#    def testBin6(self):
-#        assert decoder.decode(
-#            ints2octs((9, 5, 162, 0, 255, 255, 1))
-#        ) == (univ.Real((1, 2, 262140)), null)
-
-#    def testBin7(self):
-#        assert decoder.decode(
-#            ints2octs((9, 7, 227, 4, 1, 35, 69, 103, 1))
-#        ) == (univ.Real((-1, 2, 76354972)), null)
+    def testLargeBinaryPrettyPrintOverflow(self):
+        value, rest = decoder.decode(
+            b'\t\t\xeb\x060662.666\xd0B\x00\x00\x00\x00\x00\x00\x00'
+        )
+        assert value.prettyPrint() == '<overflow>'
+        assert rest == b'6\xd0B\x00\x00\x00\x00\x00\x00\x00'
+
+        try:
+            float(value)
+        except OverflowError:
+            pass
+        else:
+            assert 0, '__float__() tolerated overflow'
 
     def testPlusInf(self):
         assert decoder.decode(
Index: pyasn1-0.5.0/tests/codec/cer/test_decoder.py
===================================================================
--- pyasn1-0.5.0.orig/tests/codec/cer/test_decoder.py
+++ pyasn1-0.5.0/tests/codec/cer/test_decoder.py
@@ -14,6 +14,7 @@ from pyasn1.type import namedtype
 from pyasn1.type import opentype
 from pyasn1.type import univ
 from pyasn1.codec.cer import decoder
+from pyasn1.codec.cer import encoder
 from pyasn1.compat.octets import ints2octs, str2octs, null
 from pyasn1.error import PyAsn1Error
 
@@ -81,6 +82,15 @@ class LargeTagDecoderTestCase(BaseTestCa
             assert 0, 'excessive long tag tolerated'
 
 
+class RealDecoderTestCase(BaseTestCase):
+    def testLargeBinaryRoundTrip(self):
+        substrate = encoder.encode(univ.Real((-1, 2, 76354972)))
+        value, rest = decoder.decode(substrate)
+
+        assert tuple(value) == (-1, 2, 76354972)
+        assert rest == b''
+
+
 class SequenceDecoderWithUntaggedOpenTypesTestCase(BaseTestCase):
     def setUp(self):
         openType = opentype.OpenType(
Index: pyasn1-0.5.0/tests/codec/der/test_decoder.py
===================================================================
--- pyasn1-0.5.0.orig/tests/codec/der/test_decoder.py
+++ pyasn1-0.5.0/tests/codec/der/test_decoder.py
@@ -14,6 +14,7 @@ from pyasn1.type import namedtype
 from pyasn1.type import opentype
 from pyasn1.type import univ
 from pyasn1.codec.der import decoder
+from pyasn1.codec.der import encoder
 from pyasn1.compat.octets import ints2octs, null
 from pyasn1.error import PyAsn1Error
 
@@ -87,6 +88,24 @@ class LargeTagDecoderTestCase(BaseTestCa
             assert 0, 'excessive long tag tolerated'
 
 
+class RealDecoderTestCase(BaseTestCase):
+    def testCanonicalLargeBinaryReal(self):
+        substrate = encoder.encode(univ.Real((1, 2, 1000000)))
+        assert substrate == bytes((9, 5, 0x82, 0x0f, 0x42, 0x40, 1))
+
+        value, rest = decoder.decode(substrate)
+
+        assert tuple(value) == (1, 2, 1000000)
+        assert rest == b''
+
+    def testLargeBinaryRoundTrip(self):
+        substrate = encoder.encode(univ.Real((-1, 2, 76354972)))
+        value, rest = decoder.decode(substrate)
+
+        assert tuple(value) == (-1, 2, 76354972)
+        assert rest == b''
+
+
 class SequenceDecoderWithUntaggedOpenTypesTestCase(BaseTestCase):
     def setUp(self):
         openType = opentype.OpenType(
Index: pyasn1-0.5.0/tests/type/test_univ.py
===================================================================
--- pyasn1-0.5.0.orig/tests/type/test_univ.py
+++ pyasn1-0.5.0/tests/type/test_univ.py
@@ -780,9 +780,49 @@ class RealTestCase(BaseTestCase):
     def testFloat(self):
         assert float(univ.Real(4.0)) == 4.0, '__float__() fails'
 
+    def testFloatBase10Precision(self):
+        assert float(univ.Real((3, 10, 23))) == 3e23, '__float__() lost base-10 behavior'
+
+    def testFloatOverflow(self):
+        try:
+            float(univ.Real((1, 2, 1000000)))
+        except OverflowError:
+            pass
+        else:
+            assert 0, '__float__() tolerated overflow'
+
+        assert univ.Real((1, 2, 1000000)).prettyPrint() == '<overflow>'
+
+    def testFloatUnderflow(self):
+        assert float(univ.Real((1, 2, -1000000))) == 0.0, '__float__() failed underflow'
+
+    def testFloatZeroMantissa(self):
+        assert float(univ.Real((0, 10, 1000000000))) == 0.0, '__float__() failed zero mantissa'
+        assert float(univ.Real((0, 2, 1000000000))) == 0.0, '__float__() failed zero mantissa'
+
+    def testFloatBase10Overflow(self):
+        try:
+            float(univ.Real((1, 10, sys.float_info.max_10_exp + 1)))
+        except OverflowError:
+            pass
+        else:
+            assert 0, '__float__() tolerated base-10 overflow'
+
+    def testFloatBase10NormalizedOverflow(self):
+        try:
+            float(univ.Real((10, 10, sys.float_info.max_10_exp)))
+        except OverflowError:
+            pass
+        else:
+            assert 0, '__float__() tolerated normalized base-10 overflow'
+
     def testPrettyIn(self):
         assert univ.Real((3, 10, 0)) == 3, 'prettyIn() fails'
 
+    def testPrettyInBigBase10Mantissa(self):
+        assert tuple(univ.Real((10 ** 400, 10, 0))) == (1, 10, 400), \
+            'prettyIn() big mantissa normalization fails'
+
     # infinite float values
     def testStrInf(self):
         assert str(univ.Real('inf')) == 'inf', 'str() fails'
