[m-rev.] for review: conversion of 8- and 16-bit integers to floats

Julien Fischer jfischer at opturion.com
Mon Oct 8 13:44:06 AEDT 2018


For review by anyone.

For the other integer types we will need to decide on what behaviours
we want to provide (and whether they are feasible to implement across
the target languages).  That's a separate change.

-------------------------------------------------------

Conversion of 8- and 16-bit integers to floats.

Add functions for performing conversion of 8- and 16-bit integers to floats.
Since the ranges of all these integer types lie within the what can be exactly
represented by both single- and double-precision floating-point values, these
conversions always succeed.

Add a note about the float/1 function.

library/float.m:
      Add the new conversion functions.

      Note that the behaviour of float/1 is currently undefined if the
      argument is outside the range that the floating-point type can exactly
      represent integers.

      Fix section headings.

tests/hard_coded/Mmakfile:;
tests/hard_coded/float_conv.{m,exp}
     A test of int->float conversion; currently for 8- and 16-bit integers.

Julien.

diff --git a/library/float.m b/library/float.m
index d9b3c68..ad4a3bc 100644
--- a/library/float.m
+++ b/library/float.m
@@ -58,7 +58,7 @@

  %---------------------------------------------------------------------------%
  %
-% Arithmetic functions
+% Arithmetic functions.
  %

      % Addition.
@@ -95,8 +95,9 @@

  %---------------------------------------------------------------------------%
  %
-% Comparison predicates
+% Comparison predicates.
  %
+
      % Less than.
      %
  :- pred (float::in) < (float::in) is semidet.
@@ -115,13 +116,45 @@

  %---------------------------------------------------------------------------%
  %
-% Conversion functions
+% Conversion from integer types.
  %

-    % Convert int to float.
+    % Convert an int into float.
+    %
+    % The behaviour when the int exceeds the range of what can be exactly
+    % represented by a float is undefined.
      %
  :- func float(int) = float.

+    % Convert a signed 8-bit integer into a float.
+    % Always succeeds as all signed 8-bit integers have an exact
+    % floating-point representation.
+    %
+:- func from_int8(int8) = float.
+
+    % Convert an unsigned 8-bit integer into a float.
+    % Always succeeds as all unsigned 8-bit integers have an exact
+    % floating-point representation.
+    %
+:- func from_int16(int16) = float.
+
+    % Convert a signed 16-bit integer into a float.
+    % Always succeeds as all signed 16-bit integers have an exact
+    % floating-point representation.
+    %
+:- func from_uint8(uint8) =  float.
+
+    % Convert an unsigned 16-bit integer into a float.
+    % Always succeeds as all unsigned 16-bit integers have an exact
+    % floating-point representation.
+    %
+:- func from_uint16(uint16) = float.
+
+%---------------------------------------------------------------------------%
+%
+% Conversion to int.
+%
+
      % ceiling_to_int(X) returns the smallest integer not less than X.
      %
  :- func ceiling_to_int(float) = int.
@@ -364,8 +397,9 @@ X / Y = Z :-

  %---------------------------------------------------------------------------%
  %
-% Conversion functions
+% Conversion from integer types.
  %
+
  %   For Java, overflows are not detected, so this must be tested for
  %   explicitly. So every time there's a cast to int, the bounds are
  %   checked first.
@@ -399,6 +433,135 @@ X / Y = Z :-
      FloatVal = float(IntVal)
  ").

+%---------------------------------------------------------------------------%
+
+:- pragma foreign_proc("C",
+    from_int8(Int8Val::in) = (FloatVal::out),
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
+"
+    FloatVal = Int8Val;
+").
+
+:- pragma foreign_proc("C#",
+    from_int8(Int8Val::in) = (FloatVal::out),
+    [will_not_call_mercury, promise_pure, thread_safe],
+"
+    FloatVal = (double) Int8Val;
+").
+
+:- pragma foreign_proc("Java",
+    from_int8(Int8Val::in) = (FloatVal::out),
+    [will_not_call_mercury, promise_pure, thread_safe],
+"
+    FloatVal = (double) Int8Val;
+").
+
+:- pragma foreign_proc("Erlang",
+    from_int8(Int8Val::in) = (FloatVal::out),
+    [will_not_call_mercury, promise_pure, thread_safe],
+"
+    FloatVal = float(Int8Val)
+").
+
+%---------------------------------------------------------------------------%
+
+:- pragma foreign_proc("C",
+    from_uint8(UInt8Val::in) = (FloatVal::out),
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
+"
+    FloatVal = UInt8Val;
+").
+
+:- pragma foreign_proc("C#",
+    from_uint8(UInt8Val::in) = (FloatVal::out),
+    [will_not_call_mercury, promise_pure, thread_safe],
+"
+    FloatVal = (double) UInt8Val;
+").
+
+:- pragma foreign_proc("Java",
+    from_uint8(UInt8Val::in) = (FloatVal::out),
+    [will_not_call_mercury, promise_pure, thread_safe],
+"
+    FloatVal = (double) (UInt8Val & 0xff);
+").
+
+:- pragma foreign_proc("Erlang",
+    from_uint8(UInt8Val::in) = (FloatVal::out),
+    [will_not_call_mercury, promise_pure, thread_safe],
+"
+    FloatVal = float(UInt8Val)
+").
+
+%---------------------------------------------------------------------------%
+
+:- pragma foreign_proc("C",
+    from_int16(Int16Val::in) = (FloatVal::out),
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
+"
+    FloatVal = Int16Val;
+").
+
+:- pragma foreign_proc("C#",
+    from_int16(Int16Val::in) = (FloatVal::out),
+    [will_not_call_mercury, promise_pure, thread_safe],
+"
+    FloatVal = (double) Int16Val;
+").
+
+:- pragma foreign_proc("Java",
+    from_int16(Int16Val::in) = (FloatVal::out),
+    [will_not_call_mercury, promise_pure, thread_safe],
+"
+    FloatVal = (double) Int16Val;
+").
+
+:- pragma foreign_proc("Erlang",
+    from_int16(Int16Val::in) = (FloatVal::out),
+    [will_not_call_mercury, promise_pure, thread_safe],
+"
+    FloatVal = float(Int16Val)
+").
+
+%---------------------------------------------------------------------------%
+
+:- pragma foreign_proc("C",
+    from_uint16(UInt16Val::in) = (FloatVal::out),
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
+"
+    FloatVal = UInt16Val;
+").
+
+:- pragma foreign_proc("C#",
+    from_uint16(UInt16Val::in) = (FloatVal::out),
+    [will_not_call_mercury, promise_pure, thread_safe],
+"
+    FloatVal = (double) UInt16Val;
+").
+
+:- pragma foreign_proc("Java",
+    from_uint16(UInt16Val::in) = (FloatVal::out),
+    [will_not_call_mercury, promise_pure, thread_safe],
+"
+    FloatVal = (double) (UInt16Val & 0xffff);
+").
+
+:- pragma foreign_proc("Erlang",
+    from_uint16(UInt16Val::in) = (FloatVal::out),
+    [will_not_call_mercury, promise_pure, thread_safe],
+"
+    FloatVal = float(UInt16Val)
+").
+
+%---------------------------------------------------------------------------%
+%
+% Conversion to ints.
+%
+
  :- pragma foreign_proc("C",
      ceiling_to_int(X::in) = (Ceil::out),
      [will_not_call_mercury, promise_pure, thread_safe,
@@ -547,7 +710,7 @@ X / Y = Z :-

  %---------------------------------------------------------------------------%
  %
-% Miscellaneous functions
+% Miscellaneous functions.
  %

  abs(Num) = Abs :-
@@ -745,8 +908,9 @@ is_zero(0.0).

  %---------------------------------------------------------------------------%
  %
-% System constants
+% System constants.
  %
+
  % For C, the floating-point system constants are derived from <float.h> and
  % implemented using the C interface.
  %
diff --git a/tests/hard_coded/Mmakefile b/tests/hard_coded/Mmakefile
index 7c6e8cb..d13314a 100644
--- a/tests/hard_coded/Mmakefile
+++ b/tests/hard_coded/Mmakefile
@@ -125,6 +125,7 @@ ORDINARY_PROGS = \
  	field_syntax \
  	finalise_decl \
  	finalize_to_bitmap \
+	float_conv \
  	float_field \
  	float_ground_term \
  	float_map \
diff --git a/tests/hard_coded/float_conv.exp b/tests/hard_coded/float_conv.exp
index e69de29..4147e62 100644
--- a/tests/hard_coded/float_conv.exp
+++ b/tests/hard_coded/float_conv.exp
@@ -0,0 +1,42 @@
+*** Testing int8 -> float conversion ***
+
+from_int8(-128i8) = -128.0
+from_int8(-1i8) = -1.0
+from_int8(0i8) = 0.0
+from_int8(8i8) = 8.0
+from_int8(16i8) = 16.0
+from_int8(32i8) = 32.0
+from_int8(64i8) = 64.0
+from_int8(127i8) = 127.0
+
+*** Testing uint8 -> float conversion ***
+
+from_uint8(0u8) = 0.0
+from_uint8(1u8) = 1.0
+from_uint8(2u8) = 2.0
+from_uint8(8u8) = 8.0
+from_uint8(23u8) = 23.0
+from_uint8(127u8) = 127.0
+from_uint8(128u8) = 128.0
+from_uint8(255u8) = 255.0
+
+*** Testing int16 -> float conversion ***
+
+from_int16(-32768i16) = -32768.0
+from_int16(-129i16) = -129.0
+from_int16(-128i16) = -128.0
+from_int16(-1i16) = -1.0
+from_int16(0i16) = 0.0
+from_int16(1i16) = 1.0
+from_int16(127i16) = 127.0
+from_int16(128i16) = 128.0
+from_int16(32767i16) = 32767.0
+
+*** Testing uint16 -> float conversion ***
+
+from_uint16(0u16) = 0.0
+from_uint16(1u16) = 1.0
+from_uint16(8u16) = 8.0
+from_uint16(127u16) = 127.0
+from_uint16(128u16) = 128.0
+from_uint16(32767u16) = 32767.0
diff --git a/tests/hard_coded/float_conv.m b/tests/hard_coded/float_conv.m
index e69de29..c5d50c2 100644
--- a/tests/hard_coded/float_conv.m
+++ b/tests/hard_coded/float_conv.m
@@ -0,0 +1,98 @@
+%---------------------------------------------------------------------------%
+% vim: ft=mercury ts=4 sw=4 et
+%---------------------------------------------------------------------------%
+
+:- module float_conv.
+:- interface.
+
+:- import_module io.
+
+:- pred main(io::di, io::uo) is det.
+
+%---------------------------------------------------------------------------%
+%---------------------------------------------------------------------------%
+
+:- implementation.
+
+:- import_module float.
+:- import_module list.
+:- import_module string.
+
+%---------------------------------------------------------------------------%
+
+main(!IO) :-
+    io.write_string("*** Testing int8 -> float conversion ***\n\n", !IO),
+    run_test("from_int8", float.from_int8, int8s, !IO),
+    io.write_string("\n*** Testing uint8 -> float conversion ***\n\n", !IO),
+    run_test("from_uint8", float.from_uint8, uint8s, !IO),
+    io.write_string("\n*** Testing int16 -> float conversion ***\n\n", !IO),
+    run_test("from_int16", float.from_int16, int16s, !IO),
+    io.write_string("\n*** Testing uint16 -> float conversion ***\n\n", !IO),
+    run_test("from_uint16", float.from_uint16, uint16s, !IO).
+
+:- pred run_test(string::in, (func(T) = float)::in, list(T)::in,
+    io::di, io::uo) is det.
+
+run_test(ConvDesc, ConvFunc, Ints, !IO) :-
+    list.foldl(do_test(ConvDesc, ConvFunc), Ints, !IO).
+
+:- pred do_test(string::in, (func(T) = float)::in, T::in, io::di, io::uo) is det.
+
+do_test(ConvDesc, ConvFunc, Int, !IO) :-
+    io.write_string(ConvDesc ++ "(", !IO),
+    io.write(Int, !IO),
+    io.write_string(") = ", !IO),
+    Float = ConvFunc(Int),
+    io.write_line(Float, !IO).
+
+:- func int8s = list(int8).
+
+int8s = [
+    -128i8,
+    -1i8,
+    0i8,
+    8i8,
+    16i8,
+    32i8,
+    64i8,
+    127i8
+].
+
+:- func uint8s = list(uint8).
+
+uint8s = [
+   0u8,
+   1u8,
+   2u8,
+   8u8,
+   23u8,
+   127u8,
+   128u8,
+   255u8
+].
+
+:- func int16s = list(int16).
+
+int16s = [
+    -32768i16,
+    -129i16,
+    -128i16,
+    -1i16,
+    0i16,
+    1i16,
+    127i16,
+    128i16,
+    32767i16
+].
+
+:- func uint16s = list(uint16).
+
+uint16s = [
+    0u16,
+    1u16,
+    8u16,
+    127u16,
+    128u16,
+    32767u16
+].
+


More information about the reviews mailing list