diff libinterp/operators/op-mi.cc @ 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
children 7854d5752dd2
line wrap: on
line diff
--- /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);
+}