# HG changeset patch # User John W. Eaton # Date 1595281396 14400 # Node ID ee9b1081471f4bd8fd7144e04475ed594ef4498a # Parent 6310bb8077520688461d0027a24c6a4bd1e738ad 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::max(). * pt-eval.cc (tree_evaluator::bind_ans): Display stored value. diff -r 6310bb807752 -r ee9b1081471f libinterp/octave-value/module.mk --- 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 \ diff -r 6310bb807752 -r ee9b1081471f libinterp/octave-value/ov-base.h --- 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; } diff -r 6310bb807752 -r ee9b1081471f libinterp/octave-value/ov-magic-int.cc --- /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 . +// +// 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 +// . +// +//////////////////////////////////////////////////////////////////////// + +#if defined (HAVE_CONFIG_H) +# include "config.h" +#endif + +#include +#include + +#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 +octave_value +octave_base_magic_int::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 +idx_vector +octave_base_magic_int::index_vector (bool require_integers) const +{ + octave_value tmp (double_value ()); + + return tmp.index_vector (require_integers); +} + +template +octave_value +octave_base_magic_int::resize (const dim_vector& dv, bool fill) const +{ + octave_value tmp (double_value ()); + + return tmp.resize (dv, fill); +} + +template +octave_value +octave_base_magic_int::as_double (void) const +{ + return static_cast (scalar_ref ()); +} + +template +octave_value +octave_base_magic_int::as_single (void) const +{ + return static_cast (scalar_ref ()); +} + +template +octave_value +octave_base_magic_int::as_int8 (void) const +{ + return octave_int8 (scalar_ref ()); +} + +template +octave_value +octave_base_magic_int::as_int16 (void) const +{ + return octave_int16 (scalar_ref ()); +} + +template +octave_value +octave_base_magic_int::as_int32 (void) const +{ + return octave_int32 (scalar_ref ()); +} + +template +octave_value +octave_base_magic_int::as_int64 (void) const +{ + return octave_int64 (scalar_ref ()); +} + +template +octave_value +octave_base_magic_int::as_uint8 (void) const +{ + return octave_uint8 (scalar_ref ()); +} + +template +octave_value +octave_base_magic_int::as_uint16 (void) const +{ + return octave_uint16 (scalar_ref ()); +} + +template +octave_value +octave_base_magic_int::as_uint32 (void) const +{ + return octave_uint32 (scalar_ref ()); +} + +template +octave_value +octave_base_magic_int::as_uint64 (void) const +{ + return octave_uint64 (scalar_ref ()); +} + +template +octave_value +octave_base_magic_int::diag (octave_idx_type m, octave_idx_type n) const +{ + octave_value tmp (double_value ()); + + return tmp.diag (m, n); +} + +template +octave_value +octave_base_magic_int::convert_to_str_internal (bool, bool, char type) const +{ + octave_value retval; + + int ival; + + if (scalar_ref ().value () > std::numeric_limits::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 (ival)), type); + + return retval; +} + + +template +bool +octave_base_magic_int::save_ascii (std::ostream& os) +{ + octave_value tmp (double_value ()); + + return tmp.save_ascii (os); +} + +template +bool +octave_base_magic_int::load_ascii (std::istream&) +{ + error ("octave_base_magic_int::load_ascii: internal error"); + + return false; +} + +template +bool +octave_base_magic_int::save_binary (std::ostream& os, bool save_as_floats) +{ + octave_value tmp (double_value ()); + + return tmp.save_binary (os, save_as_floats); +} + +template +bool +octave_base_magic_int::load_binary (std::istream&, bool, + octave::mach_info::float_format) +{ + error ("octave_base_magic_int::load_binary: internal error"); + + return false; +} + +template +bool +octave_base_magic_int::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 +bool +octave_base_magic_int::load_hdf5 (octave_hdf5_id, const char *) +{ +#if defined (HAVE_HDF5) + + error ("octave_base_magic_int::load_binary: internal error"); + + return false; + +#else + + warn_load ("hdf5"); + + return false; +#endif +} + +template +mxArray * +octave_base_magic_int::as_mxArray (bool interleaved) const +{ + octave_value tmp (double_value ()); + + return tmp.as_mxArray (interleaved); +} + +template +octave_value +octave_base_magic_int::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 ()); +} diff -r 6310bb807752 -r ee9b1081471f libinterp/octave-value/ov-magic-int.h --- /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 . +// +// 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 +// . +// +//////////////////////////////////////////////////////////////////////// + +#if ! defined (octave_ov_magic_int_h) +#define octave_ov_magic_int_h 1 + +#include "octave-config.h" + +#include +#include + +#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 +class +octave_base_magic_int : public octave_base_scalar +{ +public: + + octave_base_magic_int (void) + : octave_base_scalar (0) { } + + octave_base_magic_int (const T& val) + : octave_base_scalar (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:: + // qualification. Why not? + + const T& scalar_ref (void) const { return octave_base_scalar::scalar; } + + T& scalar_ref (void) { return octave_base_scalar::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 (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::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 +{ +public: + + octave_magic_uint (void) + : octave_base_magic_int (0) { } + + octave_magic_uint (const octave_uint64& val) + : octave_base_magic_int (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 +{ +public: + + octave_magic_int (void) + : octave_base_magic_int (0) { } + + octave_magic_int (const octave_int64& val) + : octave_base_magic_int (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 diff -r 6310bb807752 -r ee9b1081471f libinterp/octave-value/ov.cc --- 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); diff -r 6310bb807752 -r ee9b1081471f libinterp/octave-value/ov.h --- 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 (); } diff -r 6310bb807752 -r ee9b1081471f libinterp/operators/module.mk --- 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 \ diff -r 6310bb807752 -r ee9b1081471f libinterp/operators/op-mi.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 . +// +// 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 +// . +// +//////////////////////////////////////////////////////////////////////// + +#if defined (HAVE_CONFIG_H) +# include "config.h" +#endif + +#include + +#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 (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 (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 (std::numeric_limits::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::min (); + + return octave_value (new octave_magic_int (min_signed_ival)); + } + + return octave_value (-static_cast (ival)); +} + +static octave_value +oct_unop_signed_uplus (const octave_base_value& a) +{ + const octave_magic_int& v = dynamic_cast (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 (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); +} diff -r 6310bb807752 -r ee9b1081471f libinterp/parse-tree/lex.ll --- 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 (1) << std::numeric_limits::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::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)); diff -r 6310bb807752 -r ee9b1081471f libinterp/parse-tree/pt-eval.cc --- 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); }