Skip to content

Commit c127854

Browse files
committed
BIFs: refactor double to integer functions
Add helper function float_to_integer_helper, that checks the float result of functions such as floor, round, trunc, etc... instead of the arguments in advance. Furthermore a better upper and limit for safe double to int64 conversion has been found. Signed-off-by: Davide Bettio <davide@uninstall.it>
1 parent b124e0b commit c127854

File tree

2 files changed

+61
-60
lines changed

2 files changed

+61
-60
lines changed

src/libAtomVM/bif.c

Lines changed: 61 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,23 @@
6161

6262
#define MAX(a, b) ((a) > (b) ? (a) : (b))
6363

64+
/*
65+
* they are the max/min values, that can be converted to int64, such as:
66+
* avm_float_t fvalue;
67+
* int64_t ivalue = fvalue;
68+
* // ivalue is guarnteed to be valid (>= INT64_MIN and <= INT64_MAX)
69+
*
70+
* They have been found with few test C programs (and while playing with bits)
71+
* do not use `(avm_float_t) INT64_MIN` or `(avm_float_t) INT64_MAX`.
72+
*/
73+
#ifdef AVM_USE_SINGLE_PRECISION
74+
#define INT64_MIN_AS_AVM_FLOAT -9223372586610590720.0 // 0xDF000000 = -2^63
75+
#define INT64_MAX_AS_AVM_FLOAT 9223371761976868863.0 // 0x5F000000 = 2^63
76+
#else
77+
#define INT64_MIN_AS_AVM_FLOAT -9223372036854776832.0 // 0xC3E0000000000000 = -2^63
78+
#define INT64_MAX_AS_AVM_FLOAT 9223372036854775295.0 // 0x43DFFFFFFFFFFFFF = 2^62 * 1.1...1b
79+
#endif
80+
6481
// intn.h and term.h headers are decoupled. We check here that sign enum values are matching.
6582
_Static_assert(
6683
(int) TermPositiveInteger == (int) IntNPositiveInteger, "term/intn definition mismatch");
@@ -1246,28 +1263,37 @@ term bif_erlang_rem_2(Context *ctx, uint32_t fail_label, int live, term arg1, te
12461263
}
12471264
}
12481265

1266+
static term float_to_integer_helper(
1267+
avm_float_t fresult, Context *ctx, uint32_t fail_label, int live)
1268+
{
1269+
if (LIKELY(isfinite(fresult))) {
1270+
if ((fresult >= INT64_MIN_AS_AVM_FLOAT) && (fresult <= INT64_MAX_AS_AVM_FLOAT)) {
1271+
#if BOXED_TERMS_REQUIRED_FOR_INT64 > 1
1272+
return make_maybe_boxed_int64(ctx, fail_label, live, fresult);
1273+
#else
1274+
return make_maybe_boxed_int(ctx, fail_label, live, fresult);
1275+
#endif
1276+
}
1277+
}
1278+
1279+
RAISE_ERROR_BIF(fail_label, OVERFLOW_ATOM);
1280+
}
1281+
12491282
term bif_erlang_ceil_1(Context *ctx, uint32_t fail_label, int live, term arg1)
12501283
{
12511284
UNUSED(live);
12521285

12531286
if (term_is_float(arg1)) {
12541287
avm_float_t fvalue = term_to_float(arg1);
1255-
if ((fvalue <= INT64_MIN_AS_AVM_FLOAT) || (fvalue >= INT64_MAX_AS_AVM_FLOAT)) {
1256-
RAISE_ERROR_BIF(fail_label, OVERFLOW_ATOM);
1257-
}
12581288

1259-
avm_int64_t result;
1260-
#if AVM_USE_SINGLE_PRECISION
1261-
result = ceilf(fvalue);
1262-
#else
1263-
result = ceil(fvalue);
1264-
#endif
1289+
avm_float_t fresult;
1290+
#if AVM_USE_SINGLE_PRECISION
1291+
fresult = ceilf(fvalue);
1292+
#else
1293+
fresult = ceil(fvalue);
1294+
#endif
12651295

1266-
#if BOXED_TERMS_REQUIRED_FOR_INT64 > 1
1267-
return make_maybe_boxed_int64(ctx, fail_label, live, result);
1268-
#else
1269-
return make_maybe_boxed_int(ctx, fail_label, live, result);
1270-
#endif
1296+
return float_to_integer_helper(fresult, ctx, fail_label, live);
12711297
}
12721298

12731299
if (term_is_any_integer(arg1)) {
@@ -1284,22 +1310,15 @@ term bif_erlang_floor_1(Context *ctx, uint32_t fail_label, int live, term arg1)
12841310

12851311
if (term_is_float(arg1)) {
12861312
avm_float_t fvalue = term_to_float(arg1);
1287-
if ((fvalue <= INT64_MIN_AS_AVM_FLOAT) || (fvalue >= INT64_MAX_AS_AVM_FLOAT)) {
1288-
RAISE_ERROR_BIF(fail_label, OVERFLOW_ATOM);
1289-
}
12901313

1291-
avm_int64_t result;
1292-
#if AVM_USE_SINGLE_PRECISION
1293-
result = floorf(fvalue);
1294-
#else
1295-
result = floor(fvalue);
1296-
#endif
1314+
avm_float_t fresult;
1315+
#if AVM_USE_SINGLE_PRECISION
1316+
fresult = floorf(fvalue);
1317+
#else
1318+
fresult = floor(fvalue);
1319+
#endif
12971320

1298-
#if BOXED_TERMS_REQUIRED_FOR_INT64 > 1
1299-
return make_maybe_boxed_int64(ctx, fail_label, live, result);
1300-
#else
1301-
return make_maybe_boxed_int(ctx, fail_label, live, result);
1302-
#endif
1321+
return float_to_integer_helper(fresult, ctx, fail_label, live);
13031322
}
13041323

13051324
if (term_is_any_integer(arg1)) {
@@ -1316,22 +1335,15 @@ term bif_erlang_round_1(Context *ctx, uint32_t fail_label, int live, term arg1)
13161335

13171336
if (term_is_float(arg1)) {
13181337
avm_float_t fvalue = term_to_float(arg1);
1319-
if ((fvalue <= INT64_MIN_AS_AVM_FLOAT) || (fvalue >= INT64_MAX_AS_AVM_FLOAT)) {
1320-
RAISE_ERROR_BIF(fail_label, OVERFLOW_ATOM);
1321-
}
13221338

1323-
avm_int64_t result;
1324-
#if AVM_USE_SINGLE_PRECISION
1325-
result = llroundf(fvalue);
1326-
#else
1327-
result = llround(fvalue);
1328-
#endif
1339+
avm_float_t fresult;
1340+
#if AVM_USE_SINGLE_PRECISION
1341+
fresult = roundf(fvalue);
1342+
#else
1343+
fresult = round(fvalue);
1344+
#endif
13291345

1330-
#if BOXED_TERMS_REQUIRED_FOR_INT64 > 1
1331-
return make_maybe_boxed_int64(ctx, fail_label, live, result);
1332-
#else
1333-
return make_maybe_boxed_int(ctx, fail_label, live, result);
1334-
#endif
1346+
return float_to_integer_helper(fresult, ctx, fail_label, live);
13351347
}
13361348

13371349
if (term_is_any_integer(arg1)) {
@@ -1348,22 +1360,15 @@ term bif_erlang_trunc_1(Context *ctx, uint32_t fail_label, int live, term arg1)
13481360

13491361
if (term_is_float(arg1)) {
13501362
avm_float_t fvalue = term_to_float(arg1);
1351-
if ((fvalue <= INT64_MIN_AS_AVM_FLOAT) || (fvalue >= INT64_MAX_AS_AVM_FLOAT)) {
1352-
RAISE_ERROR_BIF(fail_label, OVERFLOW_ATOM);
1353-
}
13541363

1355-
avm_int64_t result;
1356-
#if AVM_USE_SINGLE_PRECISION
1357-
result = truncf(fvalue);
1358-
#else
1359-
result = trunc(fvalue);
1360-
#endif
1364+
avm_float_t fresult;
1365+
#if AVM_USE_SINGLE_PRECISION
1366+
fresult = truncf(fvalue);
1367+
#else
1368+
fresult = trunc(fvalue);
1369+
#endif
13611370

1362-
#if BOXED_TERMS_REQUIRED_FOR_INT64 > 1
1363-
return make_maybe_boxed_int64(ctx, fail_label, live, result);
1364-
#else
1365-
return make_maybe_boxed_int(ctx, fail_label, live, result);
1366-
#endif
1371+
return float_to_integer_helper(fresult, ctx, fail_label, live);
13671372
}
13681373

13691374
if (term_is_any_integer(arg1)) {

src/libAtomVM/term_typedef.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -125,8 +125,6 @@ typedef uint64_t avm_uint64_t;
125125
_Static_assert(sizeof(avm_float_t) == 4, "avm_float_t must be a 32-bit float");
126126
#endif
127127

128-
#define INT64_MIN_AS_AVM_FLOAT -9223372036854775808.0
129-
#define INT64_MAX_AS_AVM_FLOAT 9223372036854775808.0
130128
#else
131129
typedef double avm_float_t;
132130
#define AVM_FLOAT_FMT "%lf"
@@ -135,8 +133,6 @@ typedef uint64_t avm_uint64_t;
135133
_Static_assert(sizeof(avm_float_t) == 8, "avm_float_t must be a 64-bit float");
136134
#endif
137135

138-
#define INT64_MIN_AS_AVM_FLOAT -9223372036854775808.0
139-
#define INT64_MAX_AS_AVM_FLOAT 9223372036854775808.0
140136
#endif
141137

142138
typedef union {

0 commit comments

Comments
 (0)