changeset 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 d5f9c8911090
files libinterp/octave-value/module.mk libinterp/octave-value/ov-base.h libinterp/octave-value/ov-magic-int.cc libinterp/octave-value/ov-magic-int.h libinterp/octave-value/ov.cc libinterp/octave-value/ov.h libinterp/operators/module.mk libinterp/operators/op-mi.cc libinterp/parse-tree/lex.ll libinterp/parse-tree/pt-eval.cc
diffstat 10 files changed, 858 insertions(+), 4 deletions(-) [+]
line wrap: on
line diff
--- a/libinterp/octave-value/module.mk	Fri Jul 17 14:36:47 2020 -0700
+++ b/libinterp/octave-value/module.mk	Mon Jul 20 17:43:16 2020 -0400
@@ -52,6 +52,7 @@
   %reldir%/ov-flt-re-mat.h \
   %reldir%/ov-java.h \
   %reldir%/ov-lazy-idx.h \
+  %reldir%/ov-magic-int.h \
   %reldir%/ov-mex-fcn.h \
   %reldir%/ov-null-mat.h \
   %reldir%/ov-oncleanup.h \
@@ -116,6 +117,7 @@
   %reldir%/ov-flt-re-mat.cc \
   %reldir%/ov-java.cc \
   %reldir%/ov-lazy-idx.cc \
+  %reldir%/ov-magic-int.cc \
   %reldir%/ov-mex-fcn.cc \
   %reldir%/ov-null-mat.cc \
   %reldir%/ov-oncleanup.cc \
--- a/libinterp/octave-value/ov-base.h	Fri Jul 17 14:36:47 2020 -0700
+++ b/libinterp/octave-value/ov-base.h	Mon Jul 20 17:43:16 2020 -0400
@@ -454,6 +454,8 @@
 
   virtual bool is_true (void) const { return false; }
 
+  virtual bool is_magic_int (void) const { return false; }
+
   virtual bool isnull (void) const { return false; }
 
   virtual bool is_constant (void) const { return false; }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libinterp/octave-value/ov-magic-int.cc	Mon Jul 20 17:43:16 2020 -0400
@@ -0,0 +1,323 @@
+////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2020 The Octave Project Developers
+//
+// See the file COPYRIGHT.md in the top-level directory of this
+// distribution or <https://octave.org/copyright/>.
+//
+// This file is part of Octave.
+//
+// Octave is free software: you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Octave is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Octave; see the file COPYING.  If not, see
+// <https://www.gnu.org/licenses/>.
+//
+////////////////////////////////////////////////////////////////////////
+
+#if defined (HAVE_CONFIG_H)
+#  include "config.h"
+#endif
+
+#include <istream>
+#include <ostream>
+
+#include "oct-inttypes.h"
+
+#include "data-conv.h"
+#include "mach-info.h"
+#include "lo-specfun.h"
+#include "lo-mappers.h"
+
+#include "defun.h"
+#include "errwarn.h"
+#include "mxarray.h"
+#include "ovl.h"
+#include "oct-hdf5.h"
+#include "oct-stream.h"
+#include "ov-scalar.h"
+#include "ov-float.h"
+#include "ov-base.h"
+#include "ov-magic-int.h"
+#include "ov-base-scalar.h"
+#include "ov-re-mat.h"
+#include "ov-typeinfo.h"
+#include "pr-output.h"
+#include "xdiv.h"
+#include "xpow.h"
+#include "ops.h"
+
+#include "ls-oct-text.h"
+#include "ls-hdf5.h"
+
+// NOTE: Although there is some additional overhead, for all but the
+// simplest data type extraction operations, we convert to an
+// octave_scalar object and forward the operation to avoid code
+// duplication and ensure that operations on magic_int objects are
+// identical to operations on octave_scalar objects.  We could also
+// avoid code duplication by deriving octave_magic_int from
+// octave_scalar, but then we would need to store both the double and
+// octave_uint64 or octave_int64 values, doubling the storage
+// requirement.
+
+static octave_base_value *
+default_numeric_conv_fcn (const octave_base_value& a)
+{
+  return new octave_scalar (a.double_value ());
+}
+
+template <typename T>
+octave_value
+octave_base_magic_int<T>::do_index_op (const octave_value_list& idx,
+                                       bool resize_ok)
+{
+  octave_value tmp (double_value ());
+
+  return tmp.do_index_op (idx, resize_ok);
+}
+
+template <typename T>
+idx_vector
+octave_base_magic_int<T>::index_vector (bool require_integers) const
+{
+  octave_value tmp (double_value ());
+
+  return tmp.index_vector (require_integers);
+}
+
+template <typename T>
+octave_value
+octave_base_magic_int<T>::resize (const dim_vector& dv, bool fill) const
+{
+  octave_value tmp (double_value ());
+
+  return tmp.resize (dv, fill);
+}
+
+template <typename T>
+octave_value
+octave_base_magic_int<T>::as_double (void) const
+{
+  return static_cast<double> (scalar_ref ());
+}
+
+template <typename T>
+octave_value
+octave_base_magic_int<T>::as_single (void) const
+{
+  return static_cast<float> (scalar_ref ());
+}
+
+template <typename T>
+octave_value
+octave_base_magic_int<T>::as_int8 (void) const
+{
+  return octave_int8 (scalar_ref ());
+}
+
+template <typename T>
+octave_value
+octave_base_magic_int<T>::as_int16 (void) const
+{
+  return octave_int16 (scalar_ref ());
+}
+
+template <typename T>
+octave_value
+octave_base_magic_int<T>::as_int32 (void) const
+{
+  return octave_int32 (scalar_ref ());
+}
+
+template <typename T>
+octave_value
+octave_base_magic_int<T>::as_int64 (void) const
+{
+  return octave_int64 (scalar_ref ());
+}
+
+template <typename T>
+octave_value
+octave_base_magic_int<T>::as_uint8 (void) const
+{
+  return octave_uint8 (scalar_ref ());
+}
+
+template <typename T>
+octave_value
+octave_base_magic_int<T>::as_uint16 (void) const
+{
+  return octave_uint16 (scalar_ref ());
+}
+
+template <typename T>
+octave_value
+octave_base_magic_int<T>::as_uint32 (void) const
+{
+  return octave_uint32 (scalar_ref ());
+}
+
+template <typename T>
+octave_value
+octave_base_magic_int<T>::as_uint64 (void) const
+{
+  return octave_uint64 (scalar_ref ());
+}
+
+template <typename T>
+octave_value
+octave_base_magic_int<T>::diag (octave_idx_type m, octave_idx_type n) const
+{
+  octave_value tmp (double_value ());
+
+  return tmp.diag (m, n);
+}
+
+template <typename T>
+octave_value
+octave_base_magic_int<T>::convert_to_str_internal (bool, bool, char type) const
+{
+  octave_value retval;
+
+  int ival;
+
+  if (scalar_ref ().value () > std::numeric_limits<unsigned char>::max ())
+    {
+      // FIXME: is there something better we could do?
+
+      ival = 0;
+
+      ::warning ("range error for conversion to character value");
+    }
+  else
+    ival = scalar_ref ().value ();
+
+  retval = octave_value (std::string (1, static_cast<char> (ival)), type);
+
+  return retval;
+}
+
+
+template <typename T>
+bool
+octave_base_magic_int<T>::save_ascii (std::ostream& os)
+{
+  octave_value tmp (double_value ());
+
+  return tmp.save_ascii (os);
+}
+
+template <typename T>
+bool
+octave_base_magic_int<T>::load_ascii (std::istream&)
+{
+  error ("octave_base_magic_int<T>::load_ascii: internal error");
+
+  return false;
+}
+
+template <typename T>
+bool
+octave_base_magic_int<T>::save_binary (std::ostream& os, bool save_as_floats)
+{
+  octave_value tmp (double_value ());
+
+  return tmp.save_binary (os, save_as_floats);
+}
+
+template <typename T>
+bool
+octave_base_magic_int<T>::load_binary (std::istream&, bool,
+                                       octave::mach_info::float_format)
+{
+  error ("octave_base_magic_int<T>::load_binary: internal error");
+
+  return false;
+}
+
+template <typename T>
+bool
+octave_base_magic_int<T>::save_hdf5 (octave_hdf5_id loc_id, const char *name,
+                                     bool save_as_floats)
+{
+  bool retval = false;
+
+#if defined (HAVE_HDF5)
+
+  octave_value tmp (double_value ());
+
+  return tmp.save_hdf5 (loc_id, name, save_as_floats);
+
+#else
+  octave_unused_parameter (loc_id);
+  octave_unused_parameter (name);
+
+  warn_save ("hdf5");
+#endif
+
+  return retval;
+}
+
+template <typename T>
+bool
+octave_base_magic_int<T>::load_hdf5 (octave_hdf5_id, const char *)
+{
+#if defined (HAVE_HDF5)
+
+  error ("octave_base_magic_int<T>::load_binary: internal error");
+
+  return false;
+
+#else
+
+  warn_load ("hdf5");
+
+  return false;
+#endif
+}
+
+template <typename T>
+mxArray *
+octave_base_magic_int<T>::as_mxArray (bool interleaved) const
+{
+  octave_value tmp (double_value ());
+
+  return tmp.as_mxArray (interleaved);
+}
+
+template <typename T>
+octave_value
+octave_base_magic_int<T>::map (octave_base_value::unary_mapper_t umap) const
+{
+  octave_value tmp (double_value ());
+
+  return tmp.map (umap);
+}
+
+DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA (octave_magic_uint, "magic_uint",
+                                     "double");
+
+octave_base_value::type_conv_info
+octave_magic_uint::numeric_conversion_function (void) const
+{
+  return octave_base_value::type_conv_info (default_numeric_conv_fcn,
+                                            octave_scalar::static_type_id ());
+}
+
+DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA (octave_magic_int, "magic_int",
+                                     "double");
+
+octave_base_value::type_conv_info
+octave_magic_int::numeric_conversion_function (void) const
+{
+  return octave_base_value::type_conv_info (default_numeric_conv_fcn,
+                                            octave_scalar::static_type_id ());
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libinterp/octave-value/ov-magic-int.h	Mon Jul 20 17:43:16 2020 -0400
@@ -0,0 +1,326 @@
+////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2020 The Octave Project Developers
+//
+// See the file COPYRIGHT.md in the top-level directory of this
+// distribution or <https://octave.org/copyright/>.
+//
+// This file is part of Octave.
+//
+// Octave is free software: you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Octave is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Octave; see the file COPYING.  If not, see
+// <https://www.gnu.org/licenses/>.
+//
+////////////////////////////////////////////////////////////////////////
+
+#if ! defined (octave_ov_magic_int_h)
+#define octave_ov_magic_int_h 1
+
+#include "octave-config.h"
+
+#include <iosfwd>
+#include <string>
+
+#include "oct-inttypes.h"
+
+#include "ov-base.h"
+#include "ov-re-mat.h"
+#include "ov-base-scalar.h"
+#include "ov-typeinfo.h"
+
+class octave_value_list;
+
+// Large integer scalar values.  The uint64 or int64 value they contain may be
+// accessed without loss of precision when needed (for example, when
+// directly converted to a uint64 or int64 value).  Otherwise, they
+// behave like real scalars, so any operation on them will result in
+// type conversion.
+
+template <typename T>
+class
+octave_base_magic_int : public octave_base_scalar<T>
+{
+public:
+
+  octave_base_magic_int (void)
+    : octave_base_scalar<T> (0) { }
+
+  octave_base_magic_int (const T& val)
+    : octave_base_scalar<T> (val) { }
+
+  ~octave_base_magic_int (void) = default;
+
+  // We return an octave_matrix here instead of an octave_scalar so
+  // that in expressions like A(2,2,2) = 2 (for A previously
+  // undefined), A will be empty instead of a 1x1 object.
+  octave_base_value * empty_clone (void) const { return new octave_matrix (); }
+
+  // Although SCALAR is a protected member of the base class, it is not
+  // directly visible here without the explicit octave_base_slalar<T>::
+  // qualification.  Why not?
+
+  const T& scalar_ref (void) const { return octave_base_scalar<T>::scalar; }
+
+  T& scalar_ref (void) { return octave_base_scalar<T>::scalar; }
+
+  octave_value do_index_op (const octave_value_list& idx,
+                            bool resize_ok = false);
+
+  idx_vector index_vector (bool require_integers = false) const;
+
+  octave_value any (int = 0) const { return scalar_ref () != T (0); }
+
+  builtin_type_t builtin_type (void) const { return btyp_double; }
+
+  bool is_magic_int (void) const { return true; }
+
+  bool is_real_scalar (void) const { return true; }
+
+  bool isreal (void) const { return true; }
+
+  bool is_double_type (void) const { return true; }
+
+  bool isfloat (void) const { return true; }
+
+  int8NDArray int8_array_value (void) const
+  { return int8NDArray (dim_vector (1, 1), scalar_ref ()); }
+
+  int16NDArray int16_array_value (void) const
+  { return int16NDArray (dim_vector (1, 1), scalar_ref ()); }
+
+  int32NDArray int32_array_value (void) const
+  { return int32NDArray (dim_vector (1, 1), scalar_ref ()); }
+
+  int64NDArray int64_array_value (void) const
+  { return int64NDArray (dim_vector (1, 1), scalar_ref ()); }
+
+  uint8NDArray uint8_array_value (void) const
+  { return uint8NDArray (dim_vector (1, 1), scalar_ref ()); }
+
+  uint16NDArray uint16_array_value (void) const
+  { return uint16NDArray (dim_vector (1, 1), scalar_ref ()); }
+
+  uint32NDArray uint32_array_value (void) const
+  { return uint32NDArray (dim_vector (1, 1), scalar_ref ()); }
+
+  uint64NDArray uint64_array_value (void) const
+  { return uint64NDArray (dim_vector (1, 1), scalar_ref ()); }
+
+  octave_int8 int8_scalar_value (void) const
+  { return octave_int8 (scalar_ref ()); }
+
+  octave_int16 int16_scalar_value (void) const
+  { return octave_int16 (scalar_ref ()); }
+
+  octave_int32 int32_scalar_value (void) const
+  { return octave_int32 (scalar_ref ()); }
+
+  octave_int64 int64_scalar_value (void) const
+  { return octave_int64 (scalar_ref ()); }
+
+  octave_uint8 uint8_scalar_value (void) const
+  { return octave_uint8 (scalar_ref ()); }
+
+  octave_uint16 uint16_scalar_value (void) const
+  { return octave_uint16 (scalar_ref ()); }
+
+  octave_uint32 uint32_scalar_value (void) const
+  { return octave_uint32 (scalar_ref ()); }
+
+  octave_uint64 uint64_scalar_value (void) const
+  { return octave_uint64 (scalar_ref ()); }
+
+  double double_value (bool = false) const
+  {
+    return scalar_ref ().double_value ();
+  }
+
+  float float_value (bool = false) const
+  { return scalar_ref ().float_value (); }
+
+  double scalar_value (bool = false) const
+  { return scalar_ref ().double_value (); }
+
+  float float_scalar_value (bool = false) const
+  { return float_value (); }
+
+  Matrix matrix_value (bool = false) const
+  { return Matrix (1, 1, double_value ()); }
+
+  FloatMatrix float_matrix_value (bool = false) const
+  { return FloatMatrix (1, 1, float_value ()); }
+
+  NDArray array_value (bool = false) const
+  { return NDArray (dim_vector (1, 1), double_value ()); }
+
+  FloatNDArray float_array_value (bool = false) const
+  { return FloatNDArray (dim_vector (1, 1), float_value ()); }
+
+  SparseMatrix sparse_matrix_value (bool = false) const
+  { return SparseMatrix (Matrix (1, 1, double_value ())); }
+
+  // FIXME: Need SparseComplexMatrix (Matrix) constructor!
+  SparseComplexMatrix sparse_complex_matrix_value (bool = false) const
+  { return SparseComplexMatrix (sparse_matrix_value ()); }
+
+  octave_value resize (const dim_vector& dv, bool fill = false) const;
+
+  Complex complex_value (bool = false) const { return double_value (); }
+
+  FloatComplex float_complex_value (bool = false) const
+  { return FloatComplex (float_value ()); }
+
+  ComplexMatrix complex_matrix_value (bool = false) const
+  { return ComplexMatrix (1, 1, Complex (double_value ())); }
+
+  FloatComplexMatrix float_complex_matrix_value (bool = false) const
+  { return FloatComplexMatrix (1, 1, FloatComplex (float_value ())); }
+
+  ComplexNDArray complex_array_value (bool = false) const
+  { return ComplexNDArray (dim_vector (1, 1), Complex (double_value ())); }
+
+  FloatComplexNDArray float_complex_array_value (bool = false) const
+  {
+    return FloatComplexNDArray (dim_vector (1, 1),
+                                FloatComplex (float_value ()));
+  }
+
+  charNDArray
+  char_array_value (bool = false) const
+  {
+    charNDArray retval (dim_vector (1, 1));
+    retval(0) = static_cast<char> (scalar_ref ().char_value ());
+    return retval;
+  }
+
+  bool bool_value (bool warn = false) const
+  {
+    if (warn && scalar_ref () != T (0) && scalar_ref () != T (1))
+      warn_logical_conversion ();
+
+    return scalar_ref ().bool_value ();
+  }
+
+  boolNDArray bool_array_value (bool warn = false) const
+  {
+    if (warn && scalar_ref () != T (0) && scalar_ref () != T (1))
+      warn_logical_conversion ();
+
+    return boolNDArray (dim_vector (1, 1), scalar_ref ().bool_value ());
+  }
+
+  octave_value as_double (void) const;
+  octave_value as_single (void) const;
+
+  octave_value as_int8 (void) const;
+  octave_value as_int16 (void) const;
+  octave_value as_int32 (void) const;
+  octave_value as_int64 (void) const;
+
+  octave_value as_uint8 (void) const;
+  octave_value as_uint16 (void) const;
+  octave_value as_uint32 (void) const;
+  octave_value as_uint64 (void) const;
+
+  // We don't need to override both forms of the diag method.  The using
+  // declaration will avoid warnings about partially-overloaded virtual
+  // functions.
+  using octave_base_scalar<T>::diag;
+
+  octave_value diag (octave_idx_type m, octave_idx_type n) const;
+
+  octave_value convert_to_str_internal (bool pad, bool force, char type) const;
+
+  void increment (void) { scalar_ref () += T (1); }
+
+  void decrement (void) { scalar_ref () -= T (1); }
+
+  bool save_ascii (std::ostream& os);
+
+  bool load_ascii (std::istream& is);
+
+  bool save_binary (std::ostream& os, bool save_as_floats);
+
+  bool load_binary (std::istream& is, bool swap,
+                    octave::mach_info::float_format fmt);
+
+  bool save_hdf5 (octave_hdf5_id loc_id, const char *name, bool save_as_floats);
+
+  bool load_hdf5 (octave_hdf5_id loc_id, const char *name);
+
+  int write (octave::stream& os, int block_size,
+             oct_data_conv::data_type output_type, int skip,
+             octave::mach_info::float_format flt_fmt) const
+  {
+    return os.write (array_value (), block_size, output_type,
+                     skip, flt_fmt);
+  }
+
+  mxArray * as_mxArray (bool interleaved) const;
+
+  octave_value map (octave_base_value::unary_mapper_t umap) const;
+};
+
+class
+OCTINTERP_API
+octave_magic_uint : public octave_base_magic_int<octave_uint64>
+{
+public:
+
+  octave_magic_uint (void)
+    : octave_base_magic_int<octave_uint64> (0) { }
+
+  octave_magic_uint (const octave_uint64& val)
+    : octave_base_magic_int<octave_uint64> (val) { }
+
+  ~octave_magic_uint (void) = default;
+
+  octave_base_value * clone (void) const
+  {
+    return new octave_magic_uint (*this);
+  }
+
+  type_conv_info numeric_conversion_function (void) const;
+
+private:
+
+  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA
+};
+
+class
+OCTINTERP_API
+octave_magic_int : public octave_base_magic_int<octave_int64>
+{
+public:
+
+  octave_magic_int (void)
+    : octave_base_magic_int<octave_int64> (0) { }
+
+  octave_magic_int (const octave_int64& val)
+    : octave_base_magic_int<octave_int64> (val) { }
+
+  ~octave_magic_int (void) = default;
+
+  octave_base_value * clone (void) const
+  {
+    return new octave_magic_int (*this);
+  }
+
+  type_conv_info numeric_conversion_function (void) const;
+
+private:
+
+  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA
+};
+
+#endif
--- a/libinterp/octave-value/ov.cc	Fri Jul 17 14:36:47 2020 -0700
+++ b/libinterp/octave-value/ov.cc	Mon Jul 20 17:43:16 2020 -0400
@@ -76,6 +76,7 @@
 #include "ov-usr-fcn.h"
 #include "ov-fcn-handle.h"
 #include "ov-typeinfo.h"
+#include "ov-magic-int.h"
 #include "ov-null-mat.h"
 #include "ov-lazy-idx.h"
 #include "ov-java.h"
@@ -2123,6 +2124,8 @@
   octave_value retval = *this;
   if (isnull ())
     retval = octave_value (rep->empty_clone ());
+  else if (is_magic_int ())
+    retval = octave_value (rep->double_value ());
   else
     retval.maybe_economize ();
 
@@ -2139,6 +2142,13 @@
         delete rep;
       rep = rc;
     }
+  else if (is_magic_int ())
+    {
+      octave_base_value *rc = new octave_scalar (rep->double_value ());
+      if (--rep->count == 0)
+        delete rep;
+      rep = rc;
+    }
   else
     maybe_economize ();
 }
@@ -3022,6 +3032,8 @@
   octave_float_complex_matrix::register_type (ti);
   octave_float_complex_diag_matrix::register_type (ti);
   octave_perm_matrix::register_type (ti);
+  octave_magic_int::register_type (ti);
+  octave_magic_uint::register_type (ti);
   octave_null_matrix::register_type (ti);
   octave_null_str::register_type (ti);
   octave_null_sq_str::register_type (ti);
--- a/libinterp/octave-value/ov.h	Fri Jul 17 14:36:47 2020 -0700
+++ b/libinterp/octave-value/ov.h	Mon Jul 20 17:43:16 2020 -0400
@@ -631,6 +631,9 @@
   bool is_magic_colon (void) const
   { return rep->is_magic_colon (); }
 
+  bool is_magic_int (void) const
+  { return rep->is_magic_int (); }
+
   bool isnull (void) const
   { return rep->isnull (); }
 
--- a/libinterp/operators/module.mk	Fri Jul 17 14:36:47 2020 -0700
+++ b/libinterp/operators/module.mk	Mon Jul 20 17:43:16 2020 -0400
@@ -86,6 +86,7 @@
   %reldir%/op-m-s.cc \
   %reldir%/op-m-scm.cc \
   %reldir%/op-m-sm.cc \
+  %reldir%/op-mi.cc \
   %reldir%/op-pm-cm.cc \
   %reldir%/op-pm-fcm.cc \
   %reldir%/op-pm-fm.cc \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libinterp/operators/op-mi.cc	Mon Jul 20 17:43:16 2020 -0400
@@ -0,0 +1,119 @@
+////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2020 The Octave Project Developers
+//
+// See the file COPYRIGHT.md in the top-level directory of this
+// distribution or <https://octave.org/copyright/>.
+//
+// This file is part of Octave.
+//
+// Octave is free software: you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Octave is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Octave; see the file COPYING.  If not, see
+// <https://www.gnu.org/licenses/>.
+//
+////////////////////////////////////////////////////////////////////////
+
+#if defined (HAVE_CONFIG_H)
+#  include "config.h"
+#endif
+
+#include <iostream>
+
+#include "errwarn.h"
+#include "ops.h"
+#include "ov-magic-int.h"
+#include "ov-typeinfo.h"
+#include "ov.h"
+
+// Magic integer unary ops.  Only + and - are allowed so that
+// expressions like
+//
+//   int64 (-9007199254740994)
+//
+// produce proper int64 constants.
+
+static octave_value
+oct_unop_unsigned_uplus (const octave_base_value& a)
+{
+  const octave_magic_uint& v = dynamic_cast<const octave_magic_uint&> (a);
+  // no-op.
+  // FIXME: but can we do this just by incrementing the reference count?
+  return octave_value (v.clone ());
+}
+
+static octave_value
+oct_unop_unsigned_uminus (const octave_base_value& a)
+{
+  const octave_magic_uint& v = dynamic_cast<const octave_magic_uint&> (a);
+
+  // We are storing a uint64 value, so some fakery is needed here.
+  // Is there a better way?
+
+  octave_uint64 val = v.uint64_scalar_value ();
+
+  uint64_t ival = val.value ();
+
+  static const uint64_t max_val
+    = static_cast<uint64_t> (std::numeric_limits<int64_t>::max ());
+
+  static const uint64_t max_val_p1 = max_val + 1;
+
+  if (ival <= max_val)
+    {
+      int64_t signed_ival = ival;
+      return octave_value (new octave_magic_int (-signed_ival));
+    }
+
+  if (ival == max_val_p1)
+    {
+      // Correctly capture intmin.  For example, negating uint8(128)
+      // should return int8(-128) but converting directly to int8 and
+      // negating will not return the correct result.
+
+      static const int64_t min_signed_ival
+        = std::numeric_limits<int64_t>::min ();
+
+      return octave_value (new octave_magic_int (min_signed_ival));
+    }
+
+  return octave_value (-static_cast<double> (ival));
+}
+
+static octave_value
+oct_unop_signed_uplus (const octave_base_value& a)
+{
+  const octave_magic_int& v = dynamic_cast<const octave_magic_int&> (a);
+  // no-op.
+  // FIXME: but can we do this just by incrementing the reference count?
+  return octave_value (v.clone ());
+}
+
+static octave_value
+oct_unop_signed_uminus (const octave_base_value& a)
+{
+  const octave_magic_int& v = dynamic_cast<const octave_magic_int&> (a);
+
+  octave_int64 val = v.int64_scalar_value ();
+
+  return octave_value (new octave_magic_int (-val));
+}
+
+void
+install_mi_ops (octave::type_info& ti)
+{
+  INSTALL_UNOP_TI (ti, op_uplus, octave_magic_uint, unsigned_uplus);
+  INSTALL_UNOP_TI (ti, op_uminus, octave_magic_uint, unsigned_uminus);
+
+  INSTALL_UNOP_TI (ti, op_uplus, octave_magic_int, signed_uplus);
+  INSTALL_UNOP_TI (ti, op_uminus, octave_magic_int, signed_uminus);
+}
--- 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));
 
--- a/libinterp/parse-tree/pt-eval.cc	Fri Jul 17 14:36:47 2020 -0700
+++ b/libinterp/parse-tree/pt-eval.cc	Mon Jul 20 17:43:16 2020 -0400
@@ -3619,11 +3619,18 @@
           }
         else
           {
+            // FIXME: Maybe assign could also return the assigned value,
+            // just for convenience?
+
             assign (ans, val);
 
             if (print)
               {
-                octave_value_list args = ovl (val);
+                // Use varval instead of displaying VAL directly so that
+                // we get the right type and value for things like
+                // magic_int values that may mutate when stored.
+
+                octave_value_list args = ovl (varval (ans));
                 args.stash_name_tags (string_vector (ans));
                 feval ("display", args);
               }