diff --git a/lib/elixir/lib/kernel/special_forms.ex b/lib/elixir/lib/kernel/special_forms.ex index e93226a5444..b2856184840 100644 --- a/lib/elixir/lib/kernel/special_forms.ex +++ b/lib/elixir/lib/kernel/special_forms.ex @@ -233,9 +233,9 @@ defmodule Kernel.SpecialForms do Sizes for types are a bit more nuanced. The default size for integers is 8. - For floats, it is 64. For floats, `size * unit` must result in 32 or 64, + For floats, it is 64. For floats, `size * unit` must result in 16, 32, or 64, corresponding to [IEEE 754](https://en.wikipedia.org/wiki/IEEE_floating_point) - binary32 and binary64, respectively. + binary16, binary32, and binary64, respectively. For binaries, the default is the size of the binary. Only the last binary in a match can use the default size. All others must have their size specified diff --git a/lib/elixir/src/elixir_bitstring.erl b/lib/elixir/src/elixir_bitstring.erl index 3b653453440..25f88ef307e 100644 --- a/lib/elixir/src/elixir_bitstring.erl +++ b/lib/elixir/src/elixir_bitstring.erl @@ -319,8 +319,13 @@ build_spec(Meta, _Size, Unit, Type, _Endianness, Sign, Spec, E) when Type == bin build_spec(Meta, Size, Unit, Type, Endianness, Sign, Spec, E) when Type == integer; Type == float -> NumberSize = number_size(Size, Unit), if - Type == float, is_integer(NumberSize), NumberSize /= 32, NumberSize /= 64 -> - form_error(Meta, E, ?MODULE, {bittype_float_size, NumberSize}); + Type == float, is_integer(NumberSize) -> + case valid_float_size(NumberSize) of + true -> + add_spec(Type, add_spec(Endianness, add_spec(Sign, Spec))); + false -> + form_error(Meta, E, ?MODULE, {bittype_float_size, NumberSize}) + end; Size == default, Unit /= default -> form_error(Meta, E, ?MODULE, bittype_unit); true -> @@ -331,6 +336,12 @@ number_size(Size, default) when is_integer(Size) -> Size; number_size(Size, Unit) when is_integer(Size) -> Size * Unit; number_size(Size, _) -> Size. +%% TODO: Simplify when we require OTP 24 +valid_float_size(16) -> erlang:system_info(otp_release) >= "24"; +valid_float_size(32) -> true; +valid_float_size(64) -> true; +valid_float_size(_) -> false. + add_spec(default, Spec) -> Spec; add_spec(Key, Spec) -> [{Key, [], []} | Spec]. @@ -372,7 +383,12 @@ format_error(bittype_signed) -> format_error(bittype_unit) -> "integer and float types require a size specifier if the unit specifier is given"; format_error({bittype_float_size, Other}) -> - io_lib:format("float requires size*unit to be 32 or 64 (default), got: ~p", [Other]); + Message = + case erlang:system_info(otp_release) >= "24" of + true -> "16, 32, or 64"; + false -> "32 or 64" + end, + io_lib:format("float requires size*unit to be ~s (default), got: ~p", [Message, Other]); format_error({invalid_literal, Literal}) -> io_lib:format("invalid literal ~ts in <<>>", ['Elixir.Macro':to_string(Literal)]); format_error({undefined_bittype, Expr}) -> diff --git a/lib/elixir/test/elixir/kernel/expansion_test.exs b/lib/elixir/test/elixir/kernel/expansion_test.exs index 3fbffc1a159..0acba1d2ac7 100644 --- a/lib/elixir/test/elixir/kernel/expansion_test.exs +++ b/lib/elixir/test/elixir/kernel/expansion_test.exs @@ -2387,17 +2387,49 @@ defmodule Kernel.ExpansionTest do end end - test "raises for invalid size * unit for floats" do - message = ~r"float requires size\*unit to be 32 or 64 \(default\), got: 128" + # TODO: Simplify when we require OTP 24 + if System.otp_release() >= "24" do + test "16-bit floats" do + import Kernel, except: [-: 2] - assert_raise CompileError, message, fn -> - expand(quote(do: <<12.3::32*4>>)) + assert expand(quote(do: <<12.3::float-16>>)) |> clean_meta([:alignment]) == + quote(do: <<12.3::float()-size(16)>>) end - message = ~r"float requires size\*unit to be 32 or 64 \(default\), got: 256" + test "raises for invalid size * unit for floats" do + message = ~r"float requires size\*unit to be 16, 32, or 64 \(default\), got: 128" - assert_raise CompileError, message, fn -> - expand(quote(do: <<12.3::256>>)) + assert_raise CompileError, message, fn -> + expand(quote(do: <<12.3::32*4>>)) + end + + message = ~r"float requires size\*unit to be 16, 32, or 64 \(default\), got: 256" + + assert_raise CompileError, message, fn -> + expand(quote(do: <<12.3::256>>)) + end + end + else + test "16-bit floats" do + message = ~r"float requires size\*unit to be 32 or 64 \(default\), got: 16" + + assert_raise CompileError, message, fn -> + expand(quote(do: <<12.3::16>>)) + end + end + + test "raises for invalid size * unit for floats" do + message = ~r"float requires size\*unit to be 32 or 64 \(default\), got: 128" + + assert_raise CompileError, message, fn -> + expand(quote(do: <<12.3::32*4>>)) + end + + message = ~r"float requires size\*unit to be 32 or 64 \(default\), got: 256" + + assert_raise CompileError, message, fn -> + expand(quote(do: <<12.3::256>>)) + end end end