diff --git a/src/core/inttypes.c b/src/core/inttypes.c index 8cee9e67..1489d8ea 100644 --- a/src/core/inttypes.c +++ b/src/core/inttypes.c @@ -207,6 +207,37 @@ JANET_CORE_FN(cfun_it_u64_new, return janet_wrap_u64(janet_unwrap_u64(argv[0])); } +JANET_CORE_FN(cfun_to_number, + "(int/to-number value)", + "Convert an int/u64 or int/s64 to a number. Fails if the number is out of range for an int32.") { + janet_fixarity(argc, 1); + if (janet_type(argv[0]) == JANET_ABSTRACT) { + void* abst = janet_unwrap_abstract(argv[0]); + + if (janet_abstract_type(abst) == &janet_s64_type) { + int64_t value = *((int64_t*)abst); + if (value > JANET_INTMAX_INT64) { + janet_panicf("cannot convert %q to a number, must be in the range [%q, %q]", argv[0], janet_wrap_number(JANET_INTMIN_DOUBLE), janet_wrap_number(JANET_INTMAX_DOUBLE)); + } + if (value < -JANET_INTMAX_INT64) { + janet_panicf("cannot convert %q to a number, must be in the range [%q, %q]", argv[0], janet_wrap_number(JANET_INTMIN_DOUBLE), janet_wrap_number(JANET_INTMAX_DOUBLE)); + } + return janet_wrap_number((double)value); + } + + if (janet_abstract_type(abst) == &janet_u64_type) { + uint64_t value = *((uint64_t*)abst); + if (value > JANET_INTMAX_INT64) { + janet_panicf("cannot convert %q to a number, must be in the range [%q, %q]", argv[0], janet_wrap_number(JANET_INTMIN_DOUBLE), janet_wrap_number(JANET_INTMAX_DOUBLE)); + } + + return janet_wrap_number((double)value); + } + } + + janet_panicf("expected int/u64 or int/s64, got %q", argv[0]); +} + /* * Code to support polymorphic comparison. * int/u64 and int/s64 support a "compare" method that allows @@ -514,6 +545,7 @@ void janet_lib_inttypes(JanetTable *env) { JanetRegExt it_cfuns[] = { JANET_CORE_REG("int/s64", cfun_it_s64_new), JANET_CORE_REG("int/u64", cfun_it_u64_new), + JANET_CORE_REG("int/to-number", cfun_to_number), JANET_REG_END }; janet_core_cfuns_ext(env, NULL, it_cfuns); diff --git a/test/suite0006.janet b/test/suite0006.janet index e0fa6774..3c4cd551 100644 --- a/test/suite0006.janet +++ b/test/suite0006.janet @@ -39,6 +39,25 @@ (def c (u64 "32rvv_vv_vv_vv")) (def d (u64 "123456789")))) +# Conversion back to an int32 +(assert (= (int/to-number (u64 0xFaFa)) 0xFaFa)) +(assert (= (int/to-number (i64 0xFaFa)) 0xFaFa)) +(assert (= (int/to-number (u64 9007199254740991)) 9007199254740991)) +(assert (= (int/to-number (i64 9007199254740991)) 9007199254740991)) +(assert (= (int/to-number (i64 -9007199254740991)) -9007199254740991)) + +(assert-error + "u64 out of bounds for safe integer" + (int/to-number (u64 "9007199254740993")) + +(assert-error + "s64 out of bounds for safe integer" + (int/to-number (i64 "-9007199254740993")))) + +(assert-error + "int/to-number fails on non-abstract types" + (int/to-number 1)) + (assert-no-error "create some int64 bigints" (do