Skip to content

Commit 2bd12ff

Browse files
committed
Merge pull request #2038 from bettio/fix-float-conversion
opcodesswitch.h: fix float conversion in bs PUT_FLOAT These changes are made under both the "Apache 2.0" and the "GNU Lesser General Public License 2.1 or later" license terms (dual license). SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
2 parents 2090f84 + b76aace commit 2bd12ff

File tree

3 files changed

+71
-18
lines changed

3 files changed

+71
-18
lines changed

src/libAtomVM/jit.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1457,6 +1457,9 @@ static bool jit_bitstring_insert_integer(term bin, size_t offset, term value, si
14571457
static bool jit_bitstring_insert_float(term bin, size_t offset, term value, size_t n, enum BitstringFlags flags)
14581458
{
14591459
avm_float_t float_value = term_conv_to_float(value);
1460+
if (UNLIKELY(!isfinite(float_value))) {
1461+
return false;
1462+
}
14601463
if (n == 16) {
14611464
return bitstring_insert_f16(bin, offset, float_value, flags);
14621465
} else if (n == 32) {

src/libAtomVM/opcodesswitch.h

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4857,12 +4857,17 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb)
48574857
#endif
48584858

48594859
#ifdef IMPL_EXECUTE_LOOP
4860-
avm_float_t float_value;
4860+
avm_float_t float_value = 0;
4861+
bool is_number = false;
48614862
if (term_is_float(src)) {
48624863
float_value = term_to_float(src);
4864+
is_number = true;
48634865
} else if (term_is_any_integer(src)) {
4864-
float_value = (avm_float_t) term_maybe_unbox_int64(src);
4865-
} else {
4866+
float_value = term_conv_to_float(src);
4867+
is_number = isfinite(float_value);
4868+
}
4869+
4870+
if (UNLIKELY(!is_number)) {
48664871
if (fail == 0) {
48674872
RAISE_ERROR(BADARG_ATOM);
48684873
} else {
@@ -7183,18 +7188,24 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb)
71837188
break;
71847189
}
71857190
case FLOAT_ATOM: {
7186-
avm_float_t float_value;
7191+
avm_float_t float_value = 0;
7192+
bool is_number = false;
71877193
if (term_is_float(src)) {
71887194
float_value = term_to_float(src);
7195+
is_number = true;
71897196
} else if (term_is_any_integer(src)) {
7190-
float_value = (avm_float_t) term_maybe_unbox_int64(src);
7191-
} else {
7197+
float_value = term_conv_to_float(src);
7198+
is_number = isfinite(float_value);
7199+
}
7200+
7201+
if (UNLIKELY(!is_number)) {
71927202
if (fail == 0) {
71937203
RAISE_ERROR(BADARG_ATOM);
71947204
} else {
71957205
JUMP_TO_LABEL(mod, fail);
71967206
}
71977207
}
7208+
71987209
bool result;
71997210
if (size_value == 16) {
72007211
result = bitstring_insert_f16(t, offset, float_value, flags_value);

tests/erlang_tests/test_bs.erl

Lines changed: 51 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020

2121
-module(test_bs).
2222

23-
-export([start/0, id/1, join/2]).
23+
-export([start/0, ext_id/1, join/2]).
2424

2525
start() ->
2626
test_pack_small_ints({2, 61, 20}, <<23, 180>>),
@@ -631,15 +631,8 @@ test_float() ->
631631
<<66, 0, 0, 0>> = <<Int32:32/float>>,
632632

633633
% 16-bit floats are supported in OTP 24+ and AtomVM
634-
Has16BitFloats =
635-
case erlang:system_info(machine) of
636-
"BEAM" ->
637-
erlang:system_info(otp_release) >= "24";
638-
"ATOM" ->
639-
true
640-
end,
641-
if
642-
Has16BitFloats ->
634+
case has_16bit_floats() of
635+
true ->
643636
% Test that 16-bit floats work
644637
Pi16 = id(3.14),
645638
<<66, 72>> = <<Pi16:16/float>>,
@@ -649,10 +642,11 @@ test_float() ->
649642
<<Pi16B:16/float-little, 3, 14>> = <<72, 66, 3, 14>>,
650643
true = abs(Pi16B - Pi16) < 0.001,
651644
ok;
652-
true ->
645+
false ->
653646
ok
654647
end,
655648

649+
ok = test_integer_outside_float_limits(),
656650
ok = test_create_with_invalid_float_value(),
657651
ok = test_create_with_invalid_float_size(),
658652
ok.
@@ -670,14 +664,59 @@ test_create_with_invalid_float_size() ->
670664
ok = expect_error(fun() -> create_float_binary(3.14, id(foo)) end, badarg),
671665
ok.
672666

667+
test_integer_outside_float_limits() ->
668+
V = id(16#FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF),
669+
670+
FloatSize =
671+
case erlang:system_info(machine) of
672+
"BEAM" -> 8;
673+
"ATOM" -> erlang:system_info(avm_floatsize)
674+
end,
675+
676+
TestFun = fun() -> create_float_binary(V, id(64)) end,
677+
678+
case FloatSize of
679+
4 ->
680+
expect_error(TestFun, badarg);
681+
8 ->
682+
<<79, 240, 0, 0, 0, 0, 0, 0>> = TestFun(),
683+
% Following tests cannot work with 32-bit floats, since we are not able to build
684+
% an intermediate 32-bit float term.
685+
686+
% Result is inf, so it cannot be deserialized back
687+
<<127, 128, 0, 0>> = create_float_binary(V, id(32)),
688+
<<255, 128, 0, 0>> = create_float_binary(-V, id(32)),
689+
690+
% 16-bit floats are supported in OTP 24+ and AtomVM
691+
case has_16bit_floats() of
692+
true ->
693+
<<124, 0>> = create_float_binary(V, id(16)),
694+
<<252, 0>> = create_float_binary(-V, id(16)),
695+
ok;
696+
false ->
697+
ok
698+
end
699+
end,
700+
ok.
701+
673702
create_float_binary(Value, Size) ->
674703
<<Value:Size/float>>.
675704

676705
check_x86_64_jt(<<>>) -> ok;
677706
check_x86_64_jt(<<16#e9, _Offset:32/little, Tail/binary>>) -> check_x86_64_jt(Tail);
678707
check_x86_64_jt(Bin) -> {unexpected, Bin}.
679708

680-
id(X) -> X.
709+
id(X) -> ?MODULE:ext_id(X).
710+
711+
ext_id(X) -> X.
681712

682713
join(X, Y) ->
683714
<<X/binary, Y/binary>>.
715+
716+
has_16bit_floats() ->
717+
case erlang:system_info(machine) of
718+
"BEAM" ->
719+
erlang:system_info(otp_release) >= "24";
720+
"ATOM" ->
721+
true
722+
end.

0 commit comments

Comments
 (0)