diff --git a/compiler/test/stdlib/float32.test.gr b/compiler/test/stdlib/float32.test.gr
index 395dd2579..d340fbf37 100644
--- a/compiler/test/stdlib/float32.test.gr
+++ b/compiler/test/stdlib/float32.test.gr
@@ -110,6 +110,16 @@ assert compare(nan, nan) == 0
assert compare(1.0f, nan) > 0
assert compare(nan, 1.0f) < 0
+// isFinite
+assert Float32.isFinite(NaNf) == false
+assert Float32.isFinite(Infinityf) == false
+assert Float32.isFinite(-Infinityf) == false
+assert Float32.isFinite(1.0f)
+assert Float32.isFinite(0.0f)
+assert Float32.isFinite(-1.0f)
+assert Float32.isFinite(25.76f)
+assert Float32.isFinite(-25.00f)
+
// isNaN
assert Float32.isNaN(NaNf)
assert Float32.isNaN(1.0f) == false
@@ -205,3 +215,221 @@ assert Float32.isNaN(Float32.copySign(NaNf, 1.0f))
assert Float32.isNaN(Float32.copySign(NaNf, -1.0f))
assert Float32.copySign(1.0f, NaNf) == 1.0f
assert Float32.copySign(1.0f, -NaNf) == -1.0f
+
+// Float32.isClose
+assert Float32.isClose(1.0f, 1.0f)
+assert Float32.isClose(
+ 1.0f,
+ 1.0f,
+ relativeTolerance=0.5f,
+ absoluteTolerance=0.5f
+)
+assert Float32.isClose(
+ 1.0f,
+ 1.0f,
+ relativeTolerance=0.0f,
+ absoluteTolerance=0.0f
+)
+assert Float32.isClose(0.0f, 0.0f)
+assert Float32.isClose(
+ 0.0f,
+ 0.0f,
+ relativeTolerance=0.5f,
+ absoluteTolerance=0.5f
+)
+assert Float32.isClose(
+ 0.0f,
+ 0.0f,
+ relativeTolerance=0.0f,
+ absoluteTolerance=0.0f
+)
+assert Float32.isClose(0.0f, 0.1f) == false
+assert Float32.isClose(0.0f, 0.000000001f) == false
+assert Float32.isClose(0.0f, 0.00000001f, absoluteTolerance=1e-9f) == false
+assert Float32.isClose(0.0f, 0.000000001f, absoluteTolerance=1e-9f)
+assert Float32.isClose(-0.0f, 0.000000001f) == false
+assert Float32.isClose(-0.0f, 0.00000001f, absoluteTolerance=1e-9f) == false
+assert Float32.isClose(-0.0f, 0.000000001f, absoluteTolerance=1e-9f)
+assert Float32.isClose(1.1f, 1.1000001f, absoluteTolerance=1e-10f) == false
+assert Float32.isClose(1.1f, 1.100000001f, absoluteTolerance=1e-9f)
+assert Float32.isClose(Infinityf, Infinityf)
+assert Float32.isClose(-Infinityf, -Infinityf)
+assert Float32.isClose(Infinityf, -Infinityf) == false
+assert Float32.isClose(NaNf, NaNf) == false
+
+// Float32.sin - 0 to pi/2
+assert Float32.sin(0.0f) == 0.0f
+assert Float32.isClose(Float32.sin(Float32.pi / 6.0f), 0.5f)
+assert Float32.isClose(
+ Float32.sin(Float32.pi / 4.0f),
+ Float32.sqrt(2.0f) / 2.0f
+)
+assert Float32.isClose(
+ Float32.sin(Float32.pi / 3.0f),
+ Float32.sqrt(3.0f) / 2.0f,
+ absoluteTolerance=1e-5f
+)
+assert Float32.isClose(Float32.sin(Float32.pi / 2.0f), 1.0f)
+// Float32.sin - pi/2 to 2pi
+assert Float32.isClose(
+ Float32.sin(2.0f * Float32.pi / 3.0f),
+ Float32.sqrt(3.0f) / 2.0f
+)
+assert Float32.isClose(
+ Float32.sin(3.0f * Float32.pi / 4.0f),
+ Float32.sqrt(2.0f) / 2.0f
+)
+assert Float32.isClose(
+ Float32.sin(5.0f * Float32.pi / 6.0f),
+ 0.5f,
+ absoluteTolerance=1e-5f
+)
+// Note: This has an absolute error of 1e-15 because `Float32.pi` is not exact
+assert Float32.isClose(Float32.sin(Float32.pi), 0.0f, absoluteTolerance=1e-6f)
+// Float32.sin - 2pi to 3pi/2
+assert Float32.isClose(
+ Float32.sin(7.0f * Float32.pi / 6.0f),
+ -0.5f,
+ absoluteTolerance=1e-5f
+)
+assert Float32.isClose(
+ Float32.sin(5.0f * Float32.pi / 4.0f),
+ Float32.sqrt(2.0f) / -2.0f,
+ absoluteTolerance=1e-5f
+)
+assert Float32.isClose(
+ Float32.sin(4.0f * Float32.pi / 3.0f),
+ Float32.sqrt(3.0f) / -2.0f,
+ absoluteTolerance=1e-5f
+)
+assert Float32.isClose(Float32.sin(3.0f * Float32.pi / 2.0f), -1.0f)
+// Float32.sin - 3pi/2 to 0
+assert Float32.isClose(
+ Float32.sin(5.0f * Float32.pi / 3.0f),
+ Float32.sqrt(3.0f) / -2.0f,
+ absoluteTolerance=1e-5f
+)
+assert Float32.isClose(
+ Float32.sin(7.0f * Float32.pi / 4.0f),
+ Float32.sqrt(2.0f) / -2.0f,
+ absoluteTolerance=1e-5f
+)
+assert Float32.isClose(
+ Float32.sin(11.0f * Float32.pi / 6.0f),
+ -0.5f,
+ absoluteTolerance=1e-5f
+)
+// Note: This has an absolute error of 1e-5 because `Float32.pi` is not exact
+assert Float32.isClose(
+ Float32.sin(2.0f * Float32.pi),
+ 0.0f,
+ absoluteTolerance=1e-5f
+)
+// Float32.sin - special cases
+assert Float32.sin(0.5f) == Float32.sin(0.5f)
+assert Float32.sin(0.25f) == Float32.sin(0.25f)
+assert Float32.isNaN(Float32.sin(Infinityf))
+assert Float32.isNaN(Float32.sin(-Infinityf))
+assert Float32.isNaN(Float32.sin(NaNf))
+
+// Float32.cos - 0 to pi/2
+assert Float32.cos(0.0f) == 1.0f
+assert Float32.isClose(
+ Float32.cos(Float32.pi / 6.0f),
+ Float32.sqrt(3.0f) / 2.0f
+)
+assert Float32.isClose(
+ Float32.cos(Float32.pi / 4.0f),
+ Float32.sqrt(2.0f) / 2.0f
+)
+assert Float32.isClose(
+ Float32.cos(Float32.pi / 3.0f),
+ 0.5f,
+ absoluteTolerance=1e-5f
+)
+// Note: This has an absolute error of 1e-5 because `Float32.pi` is not exact
+assert Float32.isClose(
+ Float32.cos(Float32.pi / 2.0f),
+ 0.0f,
+ absoluteTolerance=1e-5f
+)
+// Float32.cos - pi/2 to 2pi
+assert Float32.isClose(
+ Float32.cos(2.0f * Float32.pi / 3.0f),
+ -0.5f,
+ absoluteTolerance=1e-5f
+)
+assert Float32.isClose(
+ Float32.cos(3.0f * Float32.pi / 4.0f),
+ Float32.sqrt(2.0f) / -2.0f
+)
+assert Float32.isClose(
+ Float32.cos(5.0f * Float32.pi / 6.0f),
+ Float32.sqrt(3.0f) / -2.0f,
+ absoluteTolerance=1e-5f
+)
+assert Float32.isClose(Float32.cos(Float32.pi), -1.0f)
+// Float32.cos - 2pi to 3pi/2
+assert Float32.isClose(
+ Float32.cos(7.0f * Float32.pi / 6.0f),
+ Float32.sqrt(3.0f) / -2.0f,
+ absoluteTolerance=1e-5f
+)
+assert Float32.isClose(
+ Float32.cos(5.0f * Float32.pi / 4.0f),
+ Float32.sqrt(2.0f) / -2.0f,
+ absoluteTolerance=1e-5f
+)
+assert Float32.isClose(
+ Float32.cos(4.0f * Float32.pi / 3.0f),
+ -0.5f,
+ absoluteTolerance=1e-5f
+)
+// Note: This has an absolute error of 1e-5 because `Float32.pi` is not exact
+assert Float32.isClose(
+ Float32.cos(3.0f * Float32.pi / 2.0f),
+ 0.0f,
+ absoluteTolerance=1e-5f
+)
+// Float32.cos - 3pi/2 to 0
+assert Float32.isClose(
+ Float32.cos(5.0f * Float32.pi / 3.0f),
+ 0.5f,
+ absoluteTolerance=1e-5f
+)
+assert Float32.isClose(
+ Float32.cos(7.0f * Float32.pi / 4.0f),
+ Float32.sqrt(2.0f) / 2.0f,
+ absoluteTolerance=1e-5f
+)
+assert Float32.isClose(
+ Float32.cos(11.0f * Float32.pi / 6.0f),
+ Float32.sqrt(3.0f) / 2.0f,
+ absoluteTolerance=1e-5f
+)
+assert Float32.isClose(Float32.cos(2.0f * Float32.pi), 1.0f)
+// Float32.cos - special cases
+assert Float32.cos(0.5f) == Float32.cos(0.5f)
+assert Float32.cos(0.25f) == Float32.cos(0.25f)
+assert Float32.isNaN(Float32.cos(Infinityf))
+assert Float32.isNaN(Float32.cos(-Infinityf))
+assert Float32.isNaN(Float32.cos(NaNf))
+
+// Float32.tan - base cases
+assert Float32.tan(0.0f) == 0.0f
+assert Float32.isClose(
+ Float32.tan(Float32.pi / 6.0f),
+ 1.0f / Float32.sqrt(3.0f)
+)
+assert Float32.isClose(Float32.tan(Float32.pi / 4.0f), 1.0f)
+assert Float32.isClose(
+ Float32.tan(Float32.pi / 3.0f),
+ Float32.sqrt(3.0f),
+ absoluteTolerance=1e-5f
+)
+// Float32.tan - special cases
+assert Float32.tan(0.5f) == Float32.tan(0.5f)
+assert Float32.tan(0.25f) == Float32.tan(0.25f)
+assert Float32.isNaN(Float32.tan(Infinityf))
+assert Float32.isNaN(Float32.tan(-Infinityf))
+assert Float32.isNaN(Float32.tan(NaNf))
diff --git a/stdlib/float32.gr b/stdlib/float32.gr
index 03e73927d..eabcb7cd9 100644
--- a/stdlib/float32.gr
+++ b/stdlib/float32.gr
@@ -24,6 +24,8 @@ use Numbers.{
coerceNumberToFloat32 as fromNumber,
coerceFloat32ToNumber as toNumber,
}
+from "runtime/math/trig" include Trig
+use Trig.{ sin, cos, tan }
@unsafe
let _VALUE_OFFSET = 4n
@@ -274,6 +276,29 @@ provide let (>=) = (x: Float32, y: Float32) => {
xv >= yv
}
+/**
+ * Checks if a float is finite.
+ * All values are finite exept for NaN, infinity or negative infinity.
+ *
+ * @param x: The number to check
+ * @returns `true` if the value is finite or `false` otherwise
+ *
+ * @example Float32.isFinite(0.5f)
+ * @example Float32.isFinite(1.0f)
+ * @example Float32.isFinite(Infinityf) == false
+ * @example Float32.isFinite(-Infinityf) == false
+ * @example Float32.isFinite(NaNf) == false
+ *
+ * @since v0.7.0
+ */
+@unsafe
+provide let isFinite = (x: Float32) => {
+ // uses the fact that all finite floats minus themselves are zero
+ // (NaN - NaN == NaN, inf - inf == NaN,
+ // -inf - -inf == NaN, inf - -inf == inf, -inf - inf == -inf)
+ x - x == 0.0f
+}
+
/**
* Checks if the value is a float NaN value (Not A Number).
*
@@ -490,3 +515,89 @@ provide let copySign = (x: Float32, y: Float32) => {
let ptr = newFloat32(WasmF32.copySign(xv, yv))
WasmI32.toGrain(ptr): Float32
}
+
+/**
+ * Determines whether two values are considered close to each other using a relative and absolute tolerance.
+ *
+ * @param a: The first value
+ * @param b: The second value
+ * @param relativeTolerance: The maximum tolerance to use relative to the larger absolute value `a` or `b`
+ * @param absoluteTolerance: The absolute tolerance to use, regardless of the values of `a` or `b`
+ * @returns `true` if the values are considered close to each other or `false` otherwise
+ *
+ * @example Float32.isClose(1.233f, 1.233f)
+ * @example Float32.isClose(1.233f, 1.233000001f)
+ * @example Float32.isClose(8.005f, 8.450f, absoluteTolerance=0.5f)
+ * @example Float32.isClose(4.0f, 4.1f, relativeTolerance=0.025f)
+ * @example Float32.isClose(1.233f, 1.24f) == false
+ * @example Float32.isClose(1.233f, 1.4566f) == false
+ * @example Float32.isClose(8.005f, 8.450f, absoluteTolerance=0.4f) == false
+ * @example Float32.isClose(4.0f, 4.1f, relativeTolerance=0.024f) == false
+ *
+ * @since v0.7.0
+ */
+provide let isClose = (a, b, relativeTolerance=1e-9f, absoluteTolerance=0.0f) => {
+ if (a == b) {
+ true
+ } else if (isFinite(a) && isFinite(b)) {
+ abs(a - b) <=
+ max(relativeTolerance * max(abs(a), abs(b)), absoluteTolerance)
+ } else {
+ // NaN and infinities which were not equal
+ false
+ }
+}
+
+/**
+ * Computes the sine of a float (in radians).
+ *
+ * @param radians: The input in radians
+ * @returns The computed sine
+ *
+ * @example Float32.sin(0.0f) == 0.0f
+ *
+ * @since v0.7.0
+ */
+@unsafe
+provide let sin = (radians: Float32) => {
+ // TODO(#2167): Implement Float32 optimized trig functions
+ let xval = WasmF32.load(WasmI32.fromGrain(radians), _VALUE_OFFSET)
+ let value = sin(WasmF64.promoteF32(xval))
+ WasmI32.toGrain(newFloat32(WasmF32.demoteF64(value))): Float32
+}
+
+/**
+ * Computes the cosine of a float (in radians).
+ *
+ * @param radians: The input in radians
+ * @returns The computed cosine
+ *
+ * @example Float32.cos(0.0f) == 1.0f
+ *
+ * @since v0.7.0
+ */
+@unsafe
+provide let cos = (radians: Float32) => {
+ // TODO(#2167): Implement Float32 optimized trig functions
+ let xval = WasmF32.load(WasmI32.fromGrain(radians), _VALUE_OFFSET)
+ let value = cos(WasmF64.promoteF32(xval))
+ WasmI32.toGrain(newFloat32(WasmF32.demoteF64(value))): Float32
+}
+
+/**
+ * Computes the tangent of a number (in radians).
+ *
+ * @param radians: The input in radians
+ * @returns The computed tangent
+ *
+ * @example Float32.tan(0.0f) == 0.0f
+ *
+ * @since v0.7.0
+ */
+@unsafe
+provide let tan = (radians: Float32) => {
+ // TODO(#2167): Implement Float32 optimized trig functions
+ let xval = WasmF32.load(WasmI32.fromGrain(radians), _VALUE_OFFSET)
+ let value = tan(WasmF64.promoteF32(xval))
+ WasmI32.toGrain(newFloat32(WasmF32.demoteF64(value))): Float32
+}
diff --git a/stdlib/float32.md b/stdlib/float32.md
index a301d8303..f68d1cbd5 100644
--- a/stdlib/float32.md
+++ b/stdlib/float32.md
@@ -513,6 +513,54 @@ use Float32.{ (>=) }
assert 3.0f >= 3.0f
```
+### Float32.**isFinite**
+
+
+Added in next
+No other changes yet.
+
+
+```grain
+isFinite : (x: Float32) => Bool
+```
+
+Checks if a float is finite.
+All values are finite exept for NaN, infinity or negative infinity.
+
+Parameters:
+
+|param|type|description|
+|-----|----|-----------|
+|`x`|`Float32`|The number to check|
+
+Returns:
+
+|type|description|
+|----|-----------|
+|`Bool`|`true` if the value is finite or `false` otherwise|
+
+Examples:
+
+```grain
+Float32.isFinite(0.5f)
+```
+
+```grain
+Float32.isFinite(1.0f)
+```
+
+```grain
+Float32.isFinite(Infinityf) == false
+```
+
+```grain
+Float32.isFinite(-Infinityf) == false
+```
+
+```grain
+Float32.isFinite(NaNf) == false
+```
+
### Float32.**isNaN**
@@ -956,3 +1004,160 @@ Float32.copySign(3.0f, -1.0f) == -3.0f
Float32.copySign(-5.0f, 1.0f) == 5.0f
```
+### Float32.**isClose**
+
+
+Added in next
+No other changes yet.
+
+
+```grain
+isClose :
+ (a: Float32, b: Float32, ?relativeTolerance: Float32,
+ ?absoluteTolerance: Float32) => Bool
+```
+
+Determines whether two values are considered close to each other using a relative and absolute tolerance.
+
+Parameters:
+
+|param|type|description|
+|-----|----|-----------|
+|`a`|`Float32`|The first value|
+|`b`|`Float32`|The second value|
+|`?relativeTolerance`|`Float32`|The maximum tolerance to use relative to the larger absolute value `a` or `b`|
+|`?absoluteTolerance`|`Float32`|The absolute tolerance to use, regardless of the values of `a` or `b`|
+
+Returns:
+
+|type|description|
+|----|-----------|
+|`Bool`|`true` if the values are considered close to each other or `false` otherwise|
+
+Examples:
+
+```grain
+Float32.isClose(1.233f, 1.233f)
+```
+
+```grain
+Float32.isClose(1.233f, 1.233000001f)
+```
+
+```grain
+Float32.isClose(8.005f, 8.450f, absoluteTolerance=0.5f)
+```
+
+```grain
+Float32.isClose(4.0f, 4.1f, relativeTolerance=0.025f)
+```
+
+```grain
+Float32.isClose(1.233f, 1.24f) == false
+```
+
+```grain
+Float32.isClose(1.233f, 1.4566f) == false
+```
+
+```grain
+Float32.isClose(8.005f, 8.450f, absoluteTolerance=0.4f) == false
+```
+
+```grain
+Float32.isClose(4.0f, 4.1f, relativeTolerance=0.024f) == false
+```
+
+### Float32.**sin**
+
+
+Added in next
+No other changes yet.
+
+
+```grain
+sin : (radians: Float32) => Float32
+```
+
+Computes the sine of a float (in radians).
+
+Parameters:
+
+|param|type|description|
+|-----|----|-----------|
+|`radians`|`Float32`|The input in radians|
+
+Returns:
+
+|type|description|
+|----|-----------|
+|`Float32`|The computed sine|
+
+Examples:
+
+```grain
+Float32.sin(0.0f) == 0.0f
+```
+
+### Float32.**cos**
+
+
+Added in next
+No other changes yet.
+
+
+```grain
+cos : (radians: Float32) => Float32
+```
+
+Computes the cosine of a float (in radians).
+
+Parameters:
+
+|param|type|description|
+|-----|----|-----------|
+|`radians`|`Float32`|The input in radians|
+
+Returns:
+
+|type|description|
+|----|-----------|
+|`Float32`|The computed cosine|
+
+Examples:
+
+```grain
+Float32.cos(0.0f) == 1.0f
+```
+
+### Float32.**tan**
+
+
+Added in next
+No other changes yet.
+
+
+```grain
+tan : (radians: Float32) => Float32
+```
+
+Computes the tangent of a number (in radians).
+
+Parameters:
+
+|param|type|description|
+|-----|----|-----------|
+|`radians`|`Float32`|The input in radians|
+
+Returns:
+
+|type|description|
+|----|-----------|
+|`Float32`|The computed tangent|
+
+Examples:
+
+```grain
+Float32.tan(0.0f) == 0.0f
+```
+