[m-dev.] integer conversions

Peter Wang novalazy at gmail.com
Tue May 26 16:16:22 AEST 2020


On Tue, 26 May 2020 08:20:59 +1000 "Zoltan Somogyi" <zoltan.somogyi at runbox.com> wrote:
> 
> 2020-05-25 17:35 GMT+10:00 Peter Wang<novalazy at gmail.com>:
> > I made a table of the predicates and functions for converting between
> > integer types (attached).
> 
> Thanks for that.
> 
> > Should we add any of the following, or others?
> 
> I think we should start by deciding what principles should govern
> what conversion predicates or functions should exist; the identities
> of those predicates/functions will then follow.
> 
> There are several dimensions on which the conversions differ.
> 
> Dimension 1 is whether the conversion can fail for some values
> of the FromType, and if so, how to we handle it. The possibilities are
> 
> 1a the conversion cannot fail, e.g. int8.to_int,
> 
> 1b the conversion can fail, and so can the predicate, e.g. int8.from_int,
> 
> 1c  the conversion can fail, but the predicate throws an exception when
>    this happens, e.g. int8.det_from_int, and
> 
> 1d the conversion can fail, but the predicate returns a mathematically
>    unsound result instead of failing or throwing, e.g. int8.cast_from_int.
> 
> Since all four variants exist, and since I don't recall anyone objecting
> to their addition, we seem to agree that all four kinds are justified in
> some cases. But we haven't discussed exactly *which* cases.
> 
> It seems clear that we shouldn't have the 1a variant for conversions
> that can fail. But should we have some or all of the 1b, 1c and 1d
> variants for conversion that cannot fail? I think the answer should be "no"
> for 1b and 1c, but there is a case for 1d, i.e. cast operations that
> happen to always produce a mathematically correct result.

I agree with your two "nos" and one "maybe".

Where the cast operation always produces a mathematically correct result,
I wish to see the non-cast function provided, e.g. uint8.to_uint.
(This is what prompted my investigation yesterday.)

> 
> Dimension 2 is whether the conversion is done by a predicate or
> function. This depends on Dimension 1. 1a, 1c and 1d are det and therefore
> can be done by a function, but 1b is semidet, and therefore should be done
> by a predicate. But should there be predicate versions of 1a, 1c and 1d
> as well? I vote no, since the presence of predicate versions complicates
> error messages, especially when higher order code is involved.

Agreed.

> 
> Dimension 3 is whether we convert from FromType to ToType
> using a predicate/function in the FromType module, such as int8.to_int,
> or one in the ToType module, such as int.from_int8. In this dimension,
> I think what we should strive for is consistency. I see three reasonable
> choices:
> 
> 3a  all conversion predicates/functions are in FromType's module ONLY
> 
> 3b all conversion predicates/functions are in ToType's module ONLY
> 
> 3c all conversion predicates/functions are in BOTH FromType's module AND
>     ToType's module.
> 
> I think option 3c has too many predicates, but reaching either 3a or 3b
> would require deleting existing predicates, including predicates
> in the 2020 stable release, which would require obsoleting them first.
> In this dimension, I have no preference.
> 

As it is, there is nearly an ordering which presumably was the
intention:

    int < uint
    int < intN
    int < uintN
    uint < uintN

where A < B means we have:

    B.from_A
    B.to_A


For the signedness conversions we have both:

    intN.cast_from_uintN    (1)
    uintN.cast_from_intN

I think we can move (1) so we have intN < uintN to match int < uint.


We also have the following conversions to/from 64-bit integer types:

    uint16.cast_from_uint64
    uint16.cast_to_uint64
    uint32.cast_from_uint64
    uint32.cast_to_uint64

It may be more intuitive if we move those functions into the uint64
instead so we have uint{16,32} < uint64; and if necessary generalise
that to  intM < intN  and  uintM < uintN  for M < N.

[Aside: we don't currently have the analogous functions for
converting between int{16,32} and int64.]


> Dimension 4 is symmetry: is we support conversion from type A to type B,
> should we support conversion from type B to type A? I vote yes.
> 

Yes.

> Dimension 5 is what type pairs we should support.
> 
> 5a conversions between int and uint
> 
> 5b conversions between int and intN, and between uint and uintN
> 
> 5c conversions between intN and uintN
> 
> 5d conversions between int and uintN, and between uint and intN
> 
> 5e conversions between intN and uintM where N != M
> 
> Dimensions 5a and 5b are done, and I think 5c is as well. I don't see
> a burning need for 5d and 5e, and they can be done in two steps
> (one to change signedness, and one to change size), so I would
> vote against these.

5a I had suggested
  - add uint.to_int
  - add uint.det_to_int

5b I had suggested
  - add uintN.to_uint
  - add uintN.from_uint
  - add uintN.cast_from_uint
  - add uintN.det_from_uint     (not in the table)

5c
  - we may wish to move signedness casts into the uintN modules, as per above

5d, 5e
  - I agree we can omit these

5f conversions between intM and intN, and between uintM and uintN, for M < N
    e.g. converting from uint8 to uint16 currently requires going
    through int (not uint as uint16.cast_from_uint is missing)

5g conversions between intM and intN, and between uintM and uintN, for M > N
    Since these deliberately lose information, only casts would need to
    be provided.

Peter


More information about the developers mailing list