diff libinterp/parse-tree/lex.ll @ 28588:ee9b1081471f

allow integer constants > flintmax to be represented exactly (bug #45945) * ov-magic-int.h, ov-magic-int.cc: New files to provide "magic" integer data type that can store integer constants larger than flintmax but that behaves like a double constant in nearly all cases. The primary exception is when the value is processed by the int64 and uint64 functions. * libinterp/octave-value/module.mk: Update. * op-mi.cc: New file. Provide unary + and - operators for magic integers so that explicit positive or negative magic integers will work as expected. * libinterp/operators/module.mk: Update. * ov-base.h (octave_base_value::is_magic_int): New virtual function. * ov.h, ov.cc (octave_value::is_magic_int): New function. (octave_value::storable_value, octave_value::make_storable_value): Also handle magic integer values. (octave_value::install_types): Install octave_magic_int and octave_magic_uint types. * lex.ll (flintmax): New static function. (base_lexer::handle_number<10>): Create magic integers from constants that contain only digits and have values in the range flintmax to std::numeric_limits<uint64_t>::max(). * pt-eval.cc (tree_evaluator::bind_ans): Display stored value.
author John W. Eaton <jwe@octave.org>
date Mon, 20 Jul 2020 17:43:16 -0400
parents 6310bb807752
children e6928695dacb
line wrap: on
line diff
--- a/libinterp/parse-tree/lex.ll	Fri Jul 17 14:36:47 2020 -0700
+++ b/libinterp/parse-tree/lex.ll	Mon Jul 20 17:43:16 2020 -0400
@@ -118,6 +118,7 @@
 #include "interpreter.h"
 #include "lex.h"
 #include "octave.h"
+#include "ov-magic-int.h"
 #include "ov.h"
 #include "parse.h"
 #include "pt-all.h"
@@ -3080,11 +3081,18 @@
     return count_token_internal (NUMBER);
   }
 
+  static uint64_t
+  flintmax (void)
+  {
+    return (static_cast<uint64_t> (1) << std::numeric_limits<double>::digits);
+  }
+
   template <>
   int
   base_lexer::handle_number<10> (void)
   {
     bool imag = false;
+    bool digits_only = true;
 
     char *yytxt = flex_yytext ();
     size_t yylng = flex_yyleng ();
@@ -3104,15 +3112,27 @@
           case 'D':
           case 'd':
             *p++ = 'e';
+            digits_only = false;
             break;
 
           case 'I':
+          case 'J':
           case 'i':
-          case 'J':
           case 'j':
+            // Octave does not provide imaginary integers.
+            digits_only = false;
             imag = true;
             break;
 
+          case '+':
+          case '-':
+          case '.':
+          case 'E':
+          case 'e':
+            digits_only = false;
+            *p++ = ch;
+            break;
+
           default:
             *p++ = ch;
             break;
@@ -3130,13 +3150,52 @@
 
     assert (nread == 1);
 
+    octave_value ov_value;
+
+    // Use >= because > will not return true until value is greater than
+    // flintmax + 2!
+
+    if (digits_only && value >= flintmax ())
+      {
+        // Try reading as an unsigned 64-bit integer.  If there is a
+        // range error, then create a double value.  Otherwise, create a
+        // special uint64 object that will be automatically converted to
+        // double unless it appears as the argument to one of the int64
+        // or uint64 functions.
+
+        errno = 0;
+        char *end;
+        uintmax_t long_int_val;
+        if (sizeof (uintmax_t) == sizeof (unsigned long long))
+          long_int_val = strtoull (tmptxt, &end, 10);
+        else if (sizeof (uintmax_t) == sizeof (unsigned long))
+          long_int_val = strtoul (tmptxt, &end, 10);
+        else
+          panic_impossible ();
+
+        if (errno != ERANGE)
+          {
+            // If possible, store the value as a signed integer.
+
+            octave_base_value *magic_int;
+            if (long_int_val > std::numeric_limits<int64_t>::max ())
+              magic_int = new octave_magic_uint (octave_uint64 (long_int_val));
+            else
+              magic_int = new octave_magic_int (octave_int64 (long_int_val));
+
+            ov_value = octave_value (magic_int);
+          }
+      }
+
     m_looking_for_object_index = false;
     m_at_beginning_of_statement = false;
 
     update_token_positions (yylng);
 
-    octave_value ov_value
-      = imag ? octave_value (Complex (0.0, value)) : octave_value (value);
+    if (ov_value.is_undefined ())
+      ov_value = (imag
+                  ? octave_value (Complex (0.0, value))
+                  : octave_value (value));
 
     push_token (new token (NUMBER, ov_value, yytxt, m_tok_beg, m_tok_end));