mirror of
https://github.com/roc-lang/roc.git
synced 2025-08-04 04:08:19 +00:00
Implement builtins for Num.isNan, Num.isInfinite, and Num.isFinite
Closes #5310 and closes #5309
This commit is contained in:
parent
d84a9fa8ba
commit
b8aaaaabda
12 changed files with 202 additions and 1 deletions
|
@ -126,6 +126,8 @@ comptime {
|
|||
num.exportSubWithOverflow(T, ROC_BUILTINS ++ "." ++ NUM ++ ".sub_with_overflow.");
|
||||
num.exportMulWithOverflow(T, T, ROC_BUILTINS ++ "." ++ NUM ++ ".mul_with_overflow.");
|
||||
|
||||
num.exportIsNan(T, ROC_BUILTINS ++ "." ++ NUM ++ ".is_nan.");
|
||||
num.exportIsInfinite(T, ROC_BUILTINS ++ "." ++ NUM ++ ".is_infinite.");
|
||||
num.exportIsFinite(T, ROC_BUILTINS ++ "." ++ NUM ++ ".is_finite.");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -95,6 +95,24 @@ pub fn exportPow(comptime T: type, comptime name: []const u8) void {
|
|||
@export(f, .{ .name = name ++ @typeName(T), .linkage = .Strong });
|
||||
}
|
||||
|
||||
pub fn exportIsNan(comptime T: type, comptime name: []const u8) void {
|
||||
comptime var f = struct {
|
||||
fn func(input: T) callconv(.C) bool {
|
||||
return std.math.isNan(input);
|
||||
}
|
||||
}.func;
|
||||
@export(f, .{ .name = name ++ @typeName(T), .linkage = .Strong });
|
||||
}
|
||||
|
||||
pub fn exportIsInfinite(comptime T: type, comptime name: []const u8) void {
|
||||
comptime var f = struct {
|
||||
fn func(input: T) callconv(.C) bool {
|
||||
return std.math.isInf(input);
|
||||
}
|
||||
}.func;
|
||||
@export(f, .{ .name = name ++ @typeName(T), .linkage = .Strong });
|
||||
}
|
||||
|
||||
pub fn exportIsFinite(comptime T: type, comptime name: []const u8) void {
|
||||
comptime var f = struct {
|
||||
fn func(input: T) callconv(.C) bool {
|
||||
|
|
|
@ -55,6 +55,9 @@ interface Num
|
|||
toFrac,
|
||||
isPositive,
|
||||
isNegative,
|
||||
isNaN,
|
||||
isInfinite,
|
||||
isFinite,
|
||||
rem,
|
||||
remChecked,
|
||||
div,
|
||||
|
@ -621,6 +624,29 @@ isNegative = \x -> x < 0
|
|||
|
||||
toFrac : Num * -> Frac *
|
||||
|
||||
## Returns `Bool.true` if the [Frac] is not a number as defined by [IEEE-754](https://en.wikipedia.org/wiki/IEEE_754)
|
||||
##
|
||||
## ```
|
||||
## Num.isNaN (0 / 0)
|
||||
## ```
|
||||
isNaN : Frac * -> Bool
|
||||
|
||||
## Returns `Bool.true` if the [Frac] is positive or negative infinity as defined by [IEEE-754](https://en.wikipedia.org/wiki/IEEE_754)
|
||||
##
|
||||
## ```
|
||||
## Num.isInfinite (1 / 0)
|
||||
##
|
||||
## Num.isInfinite (-1 / 0)
|
||||
## ```
|
||||
isInfinite : Frac * -> Bool
|
||||
|
||||
## Returns `Bool.true` if the [Frac] is not an infinity as defined by [IEEE-754](https://en.wikipedia.org/wiki/IEEE_754)
|
||||
##
|
||||
## ```
|
||||
## Num.isFinite 42
|
||||
## ```
|
||||
isFinite : Frac * -> Bool
|
||||
|
||||
## Return the absolute value of the number.
|
||||
##
|
||||
## * For a positive number, returns the same number.
|
||||
|
|
|
@ -262,6 +262,8 @@ pub const NUM_COS: IntrinsicName = float_intrinsic!("roc_builtins.num.cos");
|
|||
pub const NUM_ASIN: IntrinsicName = float_intrinsic!("roc_builtins.num.asin");
|
||||
pub const NUM_ACOS: IntrinsicName = float_intrinsic!("roc_builtins.num.acos");
|
||||
pub const NUM_ATAN: IntrinsicName = float_intrinsic!("roc_builtins.num.atan");
|
||||
pub const NUM_IS_NAN: IntrinsicName = float_intrinsic!("roc_builtins.num.is_nan");
|
||||
pub const NUM_IS_INFINITE: IntrinsicName = float_intrinsic!("roc_builtins.num.is_infinite");
|
||||
pub const NUM_IS_FINITE: IntrinsicName = float_intrinsic!("roc_builtins.num.is_finite");
|
||||
pub const NUM_LOG: IntrinsicName = float_intrinsic!("roc_builtins.num.log");
|
||||
pub const NUM_POW: IntrinsicName = float_intrinsic!("roc_builtins.num.pow");
|
||||
|
|
|
@ -184,6 +184,9 @@ map_symbol_to_lowlevel_and_arity! {
|
|||
NumLogUnchecked; NUM_LOG; 1,
|
||||
NumRound; NUM_ROUND; 1,
|
||||
NumToFrac; NUM_TO_FRAC; 1,
|
||||
NumIsNan; NUM_IS_NAN; 1,
|
||||
NumIsInfinite; NUM_IS_INFINITE; 1,
|
||||
NumIsFinite; NUM_IS_FINITE; 1,
|
||||
NumPow; NUM_POW; 2,
|
||||
NumCeiling; NUM_CEILING; 1,
|
||||
NumPowInt; NUM_POW_INT; 2,
|
||||
|
|
|
@ -885,6 +885,8 @@ pub(crate) fn run_low_level<'a, 'ctx>(
|
|||
| NumCeiling
|
||||
| NumFloor
|
||||
| NumToFrac
|
||||
| NumIsNan
|
||||
| NumIsInfinite
|
||||
| NumIsFinite
|
||||
| NumAtan
|
||||
| NumAcos
|
||||
|
@ -2351,6 +2353,10 @@ fn build_float_unary_op<'a, 'ctx>(
|
|||
"num_round",
|
||||
)
|
||||
}
|
||||
NumIsNan => call_bitcode_fn(env, &[arg.into()], &bitcode::NUM_IS_NAN[float_width]),
|
||||
NumIsInfinite => {
|
||||
call_bitcode_fn(env, &[arg.into()], &bitcode::NUM_IS_INFINITE[float_width])
|
||||
}
|
||||
NumIsFinite => call_bitcode_fn(env, &[arg.into()], &bitcode::NUM_IS_FINITE[float_width]),
|
||||
|
||||
// trigonometry
|
||||
|
|
|
@ -1613,6 +1613,8 @@ impl<'a> LowLevelCall<'a> {
|
|||
self.load_args_and_call_zig(backend, &bitcode::NUM_POW_INT[width])
|
||||
}
|
||||
|
||||
NumIsNan => num_is_nan(backend, self.arguments[0]),
|
||||
NumIsInfinite => num_is_infinite(backend, self.arguments[0]),
|
||||
NumIsFinite => num_is_finite(backend, self.arguments[0]),
|
||||
|
||||
NumAtan => match self.ret_layout_raw {
|
||||
|
@ -2167,6 +2169,112 @@ impl<'a> LowLevelCall<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Helper for NumIsNan op
|
||||
fn num_is_nan(backend: &mut WasmBackend<'_, '_>, argument: Symbol) {
|
||||
use StoredValue::*;
|
||||
let stored = backend.storage.get(&argument).to_owned();
|
||||
match stored {
|
||||
VirtualMachineStack { value_type, .. } | Local { value_type, .. } => {
|
||||
backend
|
||||
.storage
|
||||
.load_symbols(&mut backend.code_builder, &[argument]);
|
||||
match value_type {
|
||||
// Integers are never NaN. Just return False.
|
||||
ValueType::I32 | ValueType::I64 => backend.code_builder.i32_const(0),
|
||||
ValueType::F32 => {
|
||||
backend.code_builder.i32_reinterpret_f32();
|
||||
backend.code_builder.i32_const(0x7f80_0000);
|
||||
backend.code_builder.i32_and();
|
||||
backend.code_builder.i32_const(0x7f80_0000);
|
||||
backend.code_builder.i32_eq(); // Exponents are all ones
|
||||
|
||||
backend
|
||||
.storage
|
||||
.load_symbols(&mut backend.code_builder, &[argument]);
|
||||
backend.code_builder.i32_reinterpret_f32();
|
||||
backend.code_builder.i32_const(0x007f_ffff);
|
||||
backend.code_builder.i32_and();
|
||||
backend.code_builder.i32_const(0);
|
||||
backend.code_builder.i32_ne(); // Mantissa is non-zero
|
||||
backend.code_builder.i32_and();
|
||||
}
|
||||
ValueType::F64 => {
|
||||
backend.code_builder.i64_reinterpret_f64();
|
||||
backend.code_builder.i64_const(0x7ff0_0000_0000_0000);
|
||||
backend.code_builder.i64_and();
|
||||
backend.code_builder.i64_const(0x7ff0_0000_0000_0000);
|
||||
backend.code_builder.i64_eq(); // Exponents are all ones
|
||||
|
||||
backend
|
||||
.storage
|
||||
.load_symbols(&mut backend.code_builder, &[argument]);
|
||||
backend.code_builder.i64_reinterpret_f64();
|
||||
backend.code_builder.i64_const(0x000f_ffff_ffff_ffff);
|
||||
backend.code_builder.i64_and();
|
||||
backend.code_builder.i64_const(0);
|
||||
backend.code_builder.i64_ne(); // Mantissa is non-zero
|
||||
backend.code_builder.i32_and();
|
||||
}
|
||||
}
|
||||
}
|
||||
StackMemory { format, .. } => {
|
||||
match format {
|
||||
// Integers and fixed-point numbers are NaN. Just return False.
|
||||
StackMemoryFormat::Int128 | StackMemoryFormat::Decimal => {
|
||||
backend.code_builder.i32_const(0)
|
||||
}
|
||||
|
||||
StackMemoryFormat::DataStructure => {
|
||||
internal_error!("Tried to perform NumIsInfinite on a data structure")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper for NumIsInfinite op
|
||||
fn num_is_infinite(backend: &mut WasmBackend<'_, '_>, argument: Symbol) {
|
||||
use StoredValue::*;
|
||||
let stored = backend.storage.get(&argument).to_owned();
|
||||
match stored {
|
||||
VirtualMachineStack { value_type, .. } | Local { value_type, .. } => {
|
||||
backend
|
||||
.storage
|
||||
.load_symbols(&mut backend.code_builder, &[argument]);
|
||||
match value_type {
|
||||
// Integers are never infinite. Just return False.
|
||||
ValueType::I32 | ValueType::I64 => backend.code_builder.i32_const(0),
|
||||
ValueType::F32 => {
|
||||
backend.code_builder.i32_reinterpret_f32();
|
||||
backend.code_builder.i32_const(0x7fff_ffff);
|
||||
backend.code_builder.i32_and();
|
||||
backend.code_builder.i32_const(0x7f80_0000);
|
||||
backend.code_builder.i32_eq();
|
||||
}
|
||||
ValueType::F64 => {
|
||||
backend.code_builder.i64_reinterpret_f64();
|
||||
backend.code_builder.i64_const(0x7fff_ffff_ffff_ffff);
|
||||
backend.code_builder.i64_and();
|
||||
backend.code_builder.i64_const(0x7ff0_0000_0000_0000);
|
||||
backend.code_builder.i64_eq();
|
||||
}
|
||||
}
|
||||
}
|
||||
StackMemory { format, .. } => {
|
||||
match format {
|
||||
// Integers and fixed-point numbers are never infinite. Just return False.
|
||||
StackMemoryFormat::Int128 | StackMemoryFormat::Decimal => {
|
||||
backend.code_builder.i32_const(0)
|
||||
}
|
||||
|
||||
StackMemoryFormat::DataStructure => {
|
||||
internal_error!("Tried to perform NumIsInfinite on a data structure")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper for NumIsFinite op, and also part of Eq/NotEq
|
||||
fn num_is_finite(backend: &mut WasmBackend<'_, '_>, argument: Symbol) {
|
||||
use StoredValue::*;
|
||||
|
|
|
@ -86,6 +86,8 @@ pub enum LowLevel {
|
|||
NumCeiling,
|
||||
NumPowInt,
|
||||
NumFloor,
|
||||
NumIsNan,
|
||||
NumIsInfinite,
|
||||
NumIsFinite,
|
||||
NumAtan,
|
||||
NumAcos,
|
||||
|
@ -234,7 +236,6 @@ macro_rules! map_symbol_to_lowlevel {
|
|||
// these are not implemented, not sure why
|
||||
LowLevel::StrFromInt => unimplemented!(),
|
||||
LowLevel::StrFromFloat => unimplemented!(),
|
||||
LowLevel::NumIsFinite => unimplemented!(),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -314,6 +315,9 @@ map_symbol_to_lowlevel! {
|
|||
NumLogUnchecked <= NUM_LOG,
|
||||
NumRound <= NUM_ROUND,
|
||||
NumToFrac <= NUM_TO_FRAC,
|
||||
NumIsNan <= NUM_IS_NAN,
|
||||
NumIsInfinite <= NUM_IS_INFINITE,
|
||||
NumIsFinite <= NUM_IS_FINITE,
|
||||
NumPow <= NUM_POW,
|
||||
NumCeiling <= NUM_CEILING,
|
||||
NumPowInt <= NUM_POW_INT,
|
||||
|
|
|
@ -1254,6 +1254,9 @@ define_builtins! {
|
|||
153 NUM_COUNT_TRAILING_ZERO_BITS: "countTrailingZeroBits"
|
||||
154 NUM_COUNT_ONE_BITS: "countOneBits"
|
||||
155 NUM_ABS_DIFF: "absDiff"
|
||||
156 NUM_IS_NAN: "isNaN"
|
||||
157 NUM_IS_INFINITE: "isInfinite"
|
||||
158 NUM_IS_FINITE: "isFinite"
|
||||
}
|
||||
4 BOOL: "Bool" => {
|
||||
0 BOOL_BOOL: "Bool" exposed_type=true // the Bool.Bool type alias
|
||||
|
|
|
@ -1002,6 +1002,8 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[Ownership] {
|
|||
| NumFloor
|
||||
| NumToFrac
|
||||
| Not
|
||||
| NumIsNan
|
||||
| NumIsInfinite
|
||||
| NumIsFinite
|
||||
| NumAtan
|
||||
| NumAcos
|
||||
|
|
|
@ -109,6 +109,8 @@ enum FirstOrder {
|
|||
NumCeiling,
|
||||
NumPowInt,
|
||||
NumFloor,
|
||||
NumIsNan,
|
||||
NumIsInfinite,
|
||||
NumIsFinite,
|
||||
NumAtan,
|
||||
NumAcos,
|
||||
|
|
|
@ -1705,6 +1705,31 @@ fn float_to_float() {
|
|||
assert_evals_to!("Num.toFrac 0.5", 0.5, f64);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn frac_is_nan() {
|
||||
assert_evals_to!("Num.isNaN (0 / 0)", true, bool);
|
||||
assert_evals_to!("Num.isNaN (1 / 0)", false, bool);
|
||||
assert_evals_to!("Num.isNaN 42", false, bool);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn frac_is_infinite() {
|
||||
assert_evals_to!("Num.isInfinite (1 / 0)", true, bool);
|
||||
assert_evals_to!("Num.isInfinite (-1 / 0)", true, bool);
|
||||
assert_evals_to!("Num.isInfinite (0 / 0)", false, bool);
|
||||
assert_evals_to!("Num.isInfinite 42", false, bool);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn frac_is_finite() {
|
||||
assert_evals_to!("Num.isFinite 42", true, bool);
|
||||
assert_evals_to!("Num.isFinite (1 / 0)", false, bool);
|
||||
assert_evals_to!("Num.isFinite (0 / 0)", false, bool);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn int_compare() {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue