Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions lib/elixir/lib/kernel/special_forms.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
22 changes: 19 additions & 3 deletions lib/elixir/src/elixir_bitstring.erl
Original file line number Diff line number Diff line change
Expand Up @@ -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 ->
Expand All @@ -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].

Expand Down Expand Up @@ -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}) ->
Expand Down
46 changes: 39 additions & 7 deletions lib/elixir/test/elixir/kernel/expansion_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down