aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorgingerBill <gingerBill@users.noreply.github.com>2022-03-14 11:37:11 +0000
committerGitHub <noreply@github.com>2022-03-14 11:37:11 +0000
commit9fa69c3d3bc38e2c9047ddec2ccc53bcead97d54 (patch)
tree884a54b256336e7d37e773e9e37d97d729e675fa
parent743a461aa9ec46fd7f85cf8878522414241378c3 (diff)
parent7dbcaf792dec01e0c9f2725b58ac88bd0b3aeade (diff)
Merge pull request #1599 from gitlost/fract_trunc_classify_#1574
Fix for trunc_f16/32/64 (#1574)
-rw-r--r--core/math/math.odin31
-rw-r--r--examples/demo/demo.odin1
-rw-r--r--tests/common/common.odin40
-rw-r--r--tests/core/Makefile11
-rwxr-xr-x[-rw-r--r--]tests/core/crypto_hashbin621648 -> 626328 bytes
-rw-r--r--tests/core/math/linalg/glsl/test_linalg_glsl_math.odin85
-rw-r--r--tests/core/math/test_core_math.odin310
7 files changed, 471 insertions, 7 deletions
diff --git a/core/math/math.odin b/core/math/math.odin
index b81598da9..afbda051d 100644
--- a/core/math/math.odin
+++ b/core/math/math.odin
@@ -396,7 +396,7 @@ trunc_f16 :: proc "contextless" (x: f16) -> f16 {
e := (x >> shift) & mask - bias
if e < shift {
- x &= ~(1 << (shift-e)) - 1
+ x &~= 1 << (shift-e) - 1
}
return transmute(f16)x
}
@@ -428,7 +428,7 @@ trunc_f32 :: proc "contextless" (x: f32) -> f32 {
e := (x >> shift) & mask - bias
if e < shift {
- x &= ~(1 << (shift-e)) - 1
+ x &~= 1 << (shift-e) - 1
}
return transmute(f32)x
}
@@ -460,7 +460,7 @@ trunc_f64 :: proc "contextless" (x: f64) -> f64 {
e := (x >> shift) & mask - bias
if e < shift {
- x &= ~(1 << (shift-e)) - 1
+ x &~= 1 << (shift-e) - 1
}
return transmute(f64)x
}
@@ -473,6 +473,7 @@ trunc_f64 :: proc "contextless" (x: f64) -> f64 {
}
trunc_f64le :: proc "contextless" (x: f64le) -> f64le { return #force_inline f64le(trunc_f64(f64(x))) }
trunc_f64be :: proc "contextless" (x: f64be) -> f64be { return #force_inline f64be(trunc_f64(f64(x))) }
+// Removes the fractional part of the value, i.e. rounds towards zero.
trunc :: proc{
trunc_f16, trunc_f16le, trunc_f16be,
trunc_f32, trunc_f32le, trunc_f32be,
@@ -958,7 +959,7 @@ classify_f16 :: proc "contextless" (x: f16) -> Float_Class {
return .Neg_Zero
}
return .Zero
- case x*0.5 == x:
+ case x*0.25 == x:
if x < 0 {
return .Neg_Inf
}
@@ -1027,6 +1028,8 @@ classify_f64 :: proc "contextless" (x: f64) -> Float_Class {
}
classify_f64le :: proc "contextless" (x: f64le) -> Float_Class { return #force_inline classify_f64(f64(x)) }
classify_f64be :: proc "contextless" (x: f64be) -> Float_Class { return #force_inline classify_f64(f64(x)) }
+// Returns the `Float_Class` of the value, i.e. whether normal, subnormal, zero, negative zero, NaN, infinity or
+// negative infinity.
classify :: proc{
classify_f16, classify_f16le, classify_f16be,
classify_f32, classify_f32le, classify_f32be,
@@ -1715,4 +1718,22 @@ F32_BIAS :: 0x7f
F64_MASK :: 0x7ff
F64_SHIFT :: 64 - 12
-F64_BIAS :: 0x3ff \ No newline at end of file
+F64_BIAS :: 0x3ff
+
+INF_F16 :f16: 0h7C00
+NEG_INF_F16 :f16: 0hFC00
+
+SNAN_F16 :f16: 0h7C01
+QNAN_F16 :f16: 0h7E01
+
+INF_F32 :f32: 0h7F80_0000
+NEG_INF_F32 :f32: 0hFF80_0000
+
+SNAN_F32 :f32: 0hFF80_0001
+QNAN_F32 :f32: 0hFFC0_0001
+
+INF_F64 :f64: 0h7FF0_0000_0000_0000
+NEG_INF_F64 :f64: 0hFFF0_0000_0000_0000
+
+SNAN_F64 :f64: 0h7FF0_0000_0000_0001
+QNAN_F64 :f64: 0h7FF8_0000_0000_0001 \ No newline at end of file
diff --git a/examples/demo/demo.odin b/examples/demo/demo.odin
index 044860ff7..4d4490446 100644
--- a/examples/demo/demo.odin
+++ b/examples/demo/demo.odin
@@ -94,6 +94,7 @@ the_basics :: proc() {
z: f64 // `z` is typed of type `f64` (64-bit floating point number)
z = 1 // `1` is an untyped integer literal which can be implicitly converted to `f64`
// No need for any suffixes or decimal places like in other languages
+ // (with the exception of negative zero, which must be given as `-0.0`)
// CONSTANTS JUST WORK!!!
diff --git a/tests/common/common.odin b/tests/common/common.odin
new file mode 100644
index 000000000..9a715d38e
--- /dev/null
+++ b/tests/common/common.odin
@@ -0,0 +1,40 @@
+// Boilerplate for tests
+package common
+
+import "core:testing"
+import "core:fmt"
+import "core:os"
+
+TEST_count := 0
+TEST_fail := 0
+
+when ODIN_TEST {
+ expect :: testing.expect
+ log :: testing.log
+} else {
+ expect :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) {
+ TEST_count += 1
+ if !condition {
+ TEST_fail += 1
+ fmt.printf("[%v] FAIL %v\n", loc, message)
+ return
+ }
+ }
+ log :: proc(t: ^testing.T, v: any, loc := #caller_location) {
+ fmt.printf("[%v] ", loc)
+ fmt.printf("log: %v\n", v)
+ }
+}
+
+report :: proc(t: ^testing.T) {
+ if TEST_fail > 0 {
+ if TEST_fail > 1 {
+ fmt.printf("%v/%v tests successful, %v tests failed.\n", TEST_count - TEST_fail, TEST_count, TEST_fail)
+ } else {
+ fmt.printf("%v/%v tests successful, 1 test failed.\n", TEST_count - TEST_fail, TEST_count)
+ }
+ os.exit(1)
+ } else {
+ fmt.printf("%v/%v tests successful.\n", TEST_count, TEST_count)
+ }
+}
diff --git a/tests/core/Makefile b/tests/core/Makefile
index fac5b6203..1158434bf 100644
--- a/tests/core/Makefile
+++ b/tests/core/Makefile
@@ -1,7 +1,8 @@
ODIN=../../odin
PYTHON=$(shell which python3)
-all: download_test_assets image_test compress_test strings_test hash_test crypto_test noise_test encoding_test
+all: download_test_assets image_test compress_test strings_test hash_test crypto_test noise_test encoding_test \
+ math_test linalg_glsl_math_test
download_test_assets:
$(PYTHON) download_assets.py
@@ -26,4 +27,10 @@ noise_test:
encoding_test:
$(ODIN) run encoding/json -out=test_json
- $(ODIN) run encoding/varint -out=test_varint \ No newline at end of file
+ $(ODIN) run encoding/varint -out=test_varint
+
+math_test:
+ $(ODIN) run math/test_core_math.odin -out=test_core_math -collection:tests=..
+
+linalg_glsl_math_test:
+ $(ODIN) run math/linalg/glsl/test_linalg_glsl_math.odin -out=test_linalg_glsl_math -collection:tests=..
diff --git a/tests/core/crypto_hash b/tests/core/crypto_hash
index 18b85a1e8..32f8217be 100644..100755
--- a/tests/core/crypto_hash
+++ b/tests/core/crypto_hash
Binary files differ
diff --git a/tests/core/math/linalg/glsl/test_linalg_glsl_math.odin b/tests/core/math/linalg/glsl/test_linalg_glsl_math.odin
new file mode 100644
index 000000000..e0b4f5145
--- /dev/null
+++ b/tests/core/math/linalg/glsl/test_linalg_glsl_math.odin
@@ -0,0 +1,85 @@
+// Tests "linalg_glsl_math.odin" in "core:math/linalg/glsl".
+// Must be run with `-collection:tests=` flag, e.g.
+// ./odin run tests/core/math/linalg/glsl/test_linalg_glsl_math.odin -collection:tests=./tests
+package test_core_math_linalg_glsl_math
+
+import glsl "core:math/linalg/glsl"
+
+import "core:fmt"
+import "core:math"
+import "core:testing"
+import tc "tests:common"
+
+main :: proc() {
+
+ t := testing.T{}
+
+ test_fract_f32(&t)
+ test_fract_f64(&t)
+
+ tc.report(&t)
+}
+
+@test
+test_fract_f32 :: proc(t: ^testing.T) {
+
+ using math
+
+ r: f32
+
+ Datum :: struct {
+ i: int,
+ v: f32,
+ e: f32,
+ }
+ @static data := []Datum{
+ { 0, 10.5, 0.5 }, // Issue #1574 fract in linalg/glm is broken
+ { 1, -10.5, -0.5 },
+ { 2, F32_MIN, F32_MIN }, // 0x1p-126
+ { 3, -F32_MIN, -F32_MIN },
+ { 4, 0.0, 0.0 },
+ { 5, -0.0, -0.0 },
+ { 6, 1, 0.0 },
+ { 7, -1, -0.0 },
+ { 8, 0h3F80_0001, 0h3400_0000 }, // 0x1.000002p+0, 0x1p-23
+ { 9, -0h3F80_0001, -0h3400_0000 },
+ }
+
+ for d, i in data {
+ assert(i == d.i)
+ r = glsl.fract(d.v)
+ tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(%v (%h)) -> %v (%h) != %v", i, #procedure, d.v, d.v, r, r, d.e))
+ }
+}
+
+@test
+test_fract_f64 :: proc(t: ^testing.T) {
+
+ using math
+
+ r: f64
+
+ Datum :: struct {
+ i: int,
+ v: f64,
+ e: f64,
+ }
+ @static data := []Datum{
+ { 0, 10.5, 0.5 }, // Issue #1574 fract in linalg/glm is broken
+ { 1, -10.5, -0.5 },
+ { 2, F64_MIN, F64_MIN }, // 0x1p-1022
+ { 3, -F64_MIN, -F64_MIN },
+ { 4, 0.0, 0.0 },
+ { 5, -0.0, -0.0 },
+ { 6, 1, 0.0 },
+ { 7, -1, -0.0 },
+ { 8, 0h3FF0_0000_0000_0001, 0h3CB0_0000_0000_0000 }, // 0x1.0000000000001p+0, 0x1p-52
+ { 9, -0h3FF0_0000_0000_0001, -0h3CB0_0000_0000_0000 },
+ }
+
+ for d, i in data {
+ assert(i == d.i)
+ r = glsl.fract(d.v)
+ tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(%v (%h)) -> %v (%h) != %v", i, #procedure, d.v, d.v, r, r, d.e))
+ }
+}
diff --git a/tests/core/math/test_core_math.odin b/tests/core/math/test_core_math.odin
new file mode 100644
index 000000000..57da27002
--- /dev/null
+++ b/tests/core/math/test_core_math.odin
@@ -0,0 +1,310 @@
+// Tests "math.odin" in "core:math".
+// Must be run with `-collection:tests=` flag, e.g.
+// ./odin run tests/core/math/test_core_math.odin -collection:tests=./tests
+package test_core_math
+
+import "core:fmt"
+import "core:math"
+import "core:testing"
+import tc "tests:common"
+
+main :: proc() {
+ t := testing.T{}
+
+ test_classify_f16(&t)
+ test_classify_f32(&t)
+ test_classify_f64(&t)
+
+ test_trunc_f16(&t)
+ test_trunc_f32(&t)
+ test_trunc_f64(&t)
+
+ tc.report(&t)
+}
+
+@test
+test_classify_f16 :: proc(t: ^testing.T) {
+
+ using math
+ using Float_Class
+
+ r: Float_Class
+
+ Datum :: struct {
+ i: int,
+ v: f16,
+ e: math.Float_Class,
+ }
+ @static data := []Datum{
+ { 0, 1.2, Normal },
+ { 1, 0h0001, Subnormal },
+ { 2, 0.0, Zero },
+ { 3, -0.0, Neg_Zero },
+ { 4, SNAN_F16, NaN },
+ { 5, QNAN_F16, NaN },
+ { 6, INF_F16, Inf },
+ { 7, NEG_INF_F16, Neg_Inf },
+ }
+
+ for d, i in data {
+ assert(i == d.i)
+ r = classify_f16(d.v)
+ tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(%h) -> %v != %v", i, #procedure, d.v, r, d.e))
+ }
+
+ /* Check all subnormals (exponent 0, 10-bit significand non-zero) */
+ for i :u16 = 1; i < 0x400; i += 1 {
+ v :f16 = transmute(f16)i
+ r = classify_f16(v)
+ e :Float_Class: Subnormal
+ tc.expect(t, r == e, fmt.tprintf("i:%d %s(%h) -> %v != %v", i, #procedure, v, r, e))
+ }
+}
+
+@test
+test_classify_f32 :: proc(t: ^testing.T) {
+
+ using math
+ using Float_Class
+
+ r: Float_Class
+
+ Datum :: struct {
+ i: int,
+ v: f32,
+ e: math.Float_Class,
+ }
+ @static data := []Datum{
+ { 0, 1.2, Normal },
+ { 1, 0h0000_0001, Subnormal },
+ { 2, 0.0, Zero },
+ { 3, -0.0, Neg_Zero },
+ { 4, SNAN_F32, NaN },
+ { 5, QNAN_F32, NaN },
+ { 6, INF_F32, Inf },
+ { 7, NEG_INF_F32, Neg_Inf },
+ }
+
+ for d, i in data {
+ assert(i == d.i)
+ r = classify_f32(d.v)
+ tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(%h) -> %v != %v", i, #procedure, d.v, r, d.e))
+ }
+}
+
+@test
+test_classify_f64 :: proc(t: ^testing.T) {
+
+ using math
+ using Float_Class
+
+ r: Float_Class
+
+ Datum :: struct {
+ i: int,
+ v: f64,
+ e: math.Float_Class,
+ }
+ @static data := []Datum{
+ { 0, 1.2, Normal },
+ { 1, 0h0000_0000_0000_0001, Subnormal },
+ { 2, 0.0, Zero },
+ { 3, -0.0, Neg_Zero },
+ { 4, SNAN_F64, NaN },
+ { 5, QNAN_F64, NaN },
+ { 6, INF_F64, Inf },
+ { 7, NEG_INF_F64, Neg_Inf },
+ }
+
+ for d, i in data {
+ assert(i == d.i)
+ r = classify_f64(d.v)
+ tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(%h) -> %v != %v", i, #procedure, d.v, r, d.e))
+ }
+}
+
+@test
+test_trunc_f16 :: proc(t: ^testing.T) {
+
+ using math
+
+ r, v: f16
+
+ Datum :: struct {
+ i: int,
+ v: f16,
+ e: f16,
+ }
+ @static data := []Datum{
+ { 0, 10.5, 10 }, // Issue #1574 fract in linalg/glm is broken
+ { 1, -10.5, -10 },
+
+ { 2, F16_MAX, F16_MAX },
+ { 3, -F16_MAX, -F16_MAX },
+ { 4, F16_MIN, 0.0 },
+ { 5, -F16_MIN, -0.0 },
+ { 6, 0.0, 0.0 },
+ { 7, -0.0, -0.0 },
+ { 8, 1, 1 },
+ { 9, -1, -1 },
+ { 10, INF_F16, INF_F16 },
+ { 11, NEG_INF_F16, NEG_INF_F16 },
+
+ /* From https://en.wikipedia.org/wiki/Half-precision_floating-point_format */
+ { 12, 0h3C01, 1 }, // 0x1.004p+0 (smallest > 1)
+ { 13, -0h3C01, -1 },
+ { 14, 0h3BFF, 0.0 }, // 0x1.ffcp-1 (largest < 1)
+ { 15, -0h3BFF, -0.0 },
+ { 16, 0h0001, 0.0 }, // 0x0.004p-14 (smallest subnormal)
+ { 17, -0h0001, -0.0 },
+ { 18, 0h03FF, 0.0 }, // 0x0.ffcp-14 (largest subnormal)
+ { 19, -0h03FF, -0.0 },
+
+ { 20, 0hC809, -8 }, // -0x1.024p+3
+ { 21, 0h4458, 4 }, // 0x1.16p+2
+ }
+
+ for d, i in data {
+ assert(i == d.i)
+ r = trunc_f16(d.v)
+ tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(%h) -> %h != %h", i, #procedure, d.v, r, d.e))
+ }
+
+ v = SNAN_F16
+ r = trunc_f16(v)
+ tc.expect(t, is_nan_f16(r), fmt.tprintf("%s(%f) -> %f != NaN", #procedure, v, r))
+
+ v = QNAN_F16
+ r = trunc_f16(v)
+ tc.expect(t, is_nan_f16(r), fmt.tprintf("%s(%f) -> %f != NaN", #procedure, v, r))
+}
+
+@test
+test_trunc_f32 :: proc(t: ^testing.T) {
+
+ using math
+
+ r, v: f32
+
+ Datum :: struct {
+ i: int,
+ v: f32,
+ e: f32,
+ }
+ @static data := []Datum{
+ { 0, 10.5, 10 }, // Issue #1574 fract in linalg/glm is broken
+ { 1, -10.5, -10 },
+
+ { 2, F32_MAX, F32_MAX },
+ { 3, -F32_MAX, -F32_MAX },
+ { 4, F32_MIN, 0.0 },
+ { 5, -F32_MIN, -0.0 },
+ { 6, 0.0, 0.0 },
+ { 7, -0.0, -0.0 },
+ { 8, 1, 1 },
+ { 9, -1, -1 },
+ { 10, INF_F32, INF_F32 },
+ { 11, NEG_INF_F32, NEG_INF_F32 },
+
+ /* From https://en.wikipedia.org/wiki/Single-precision_floating-point_format */
+ { 12, 0h3F80_0001, 1 }, // 0x1.000002p+0 (smallest > 1)
+ { 13, -0h3F80_0001, -1 },
+ { 14, 0h3F7F_FFFF, 0.0 }, // 0x1.fffffep-1 (largest < 1)
+ { 15, -0h3F7F_FFFF, -0.0 },
+ { 16, 0h0000_0001, 0.0 }, // 0x0.000002p-126 (smallest subnormal)
+ { 17, -0h0000_0001, -0.0 },
+ { 18, 0h007F_FFFF, 0.0 }, // 0x0.fffffep-126 (largest subnormal)
+ { 19, -0h007F_FFFF, -0.0 },
+
+ /* From libc-test src/math/sanity/truncf.h */
+ { 20, 0hC101_11D0, -8 }, // -0x1.0223ap+3
+ { 21, 0h408B_0C34, 4 }, // 0x1.161868p+2
+ { 22, 0hC106_1A5A, -8 }, // -0x1.0c34b4p+3
+ { 23, 0hC0D1_0378, -6 }, // -0x1.a206fp+2
+ { 24, 0h4114_45DE, 9 }, // 0x1.288bbcp+3
+ { 25, 0h3F29_77E8, 0.0 }, // 0x1.52efdp-1
+ { 26, 0hBED0_2E64, -0.0 }, // -0x1.a05cc8p-2
+ { 27, 0h3F0F_CF7D, 0.0 }, // 0x1.1f9efap-1
+ { 28, 0h3F46_2ED8, 0.0 }, // 0x1.8c5dbp-1
+ { 29, 0hBF2D_C375, -0.0 }, // -0x1.5b86eap-1
+ }
+
+ for d, i in data {
+ assert(i == d.i)
+ r = trunc_f32(d.v)
+ tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(%h) -> %h != %h", i, #procedure, d.v, r, d.e))
+ }
+
+ v = SNAN_F32
+ r = trunc_f32(v)
+ tc.expect(t, is_nan_f32(r), fmt.tprintf("%s(%f) -> %f != NaN", #procedure, v, r))
+
+ v = QNAN_F32
+ r = trunc_f32(v)
+ tc.expect(t, is_nan_f32(r), fmt.tprintf("%s(%f) -> %f != NaN", #procedure, v, r))
+}
+
+@test
+test_trunc_f64 :: proc(t: ^testing.T) {
+
+ using math
+
+ r, v: f64
+
+ Datum :: struct {
+ i: int,
+ v: f64,
+ e: f64,
+ }
+ data := []Datum{
+ { 0, 10.5, 10 }, // Issue #1574 fract in linalg/glm is broken
+ { 1, -10.5, -10 },
+
+ { 2, F64_MAX, F64_MAX },
+ { 3, -F64_MAX, -F64_MAX },
+ { 4, F64_MIN, 0.0 },
+ { 5, -F64_MIN, -0.0 },
+ { 6, 0.0, 0.0 },
+ { 7, -0.0, -0.0 },
+ { 8, 1, 1 },
+ { 9, -1, -1 },
+ { 10, INF_F64, INF_F64 },
+ { 11, NEG_INF_F64, NEG_INF_F64 },
+
+ /* From https://en.wikipedia.org/wiki/Double-precision_floating-point_format */
+ { 12, 0h3FF0_0000_0000_0001, 1 }, // 0x1.0000000000001p+0 (smallest > 1)
+ { 13, -0h3FF0_0000_0000_0001, -1 },
+ { 14, 0h3FEF_FFFF_FFFF_FFFF, 0.0 }, // 0x1.fffffffffffffp-1 (largest < 1)
+ { 15, -0h3FEF_FFFF_FFFF_FFFF, -0.0 },
+ { 16, 0h0000_0000_0000_0001, 0.0 }, // 0x0.0000000000001p-1022 (smallest subnormal)
+ { 17, -0h0000_0000_0000_0001, -0.0 },
+ { 18, 0h000F_FFFF_FFFF_FFFF, 0.0 }, // 0x0.fffffffffffffp-1022 (largest subnormal)
+ { 19, -0h000F_FFFF_FFFF_FFFF, -0.0 },
+
+ /* From libc-test src/math/sanity/trunc.h */
+ { 20, 0hC020_2239_F3C6_A8F1, -8 }, // -0x1.02239f3c6a8f1p+3
+ { 21, 0h4011_6186_8E18_BC67, 4 }, // 0x1.161868e18bc67p+2
+ { 22, 0hC020_C34B_3E01_E6E7, -8 }, // -0x1.0c34b3e01e6e7p+3
+ { 23, 0hC01A_206F_0A19_DCC4, -6 }, // -0x1.a206f0a19dcc4p+2
+ { 24, 0h4022_88BB_B0D6_A1E6, 9 }, // 0x1.288bbb0d6a1e6p+3
+ { 25, 0h3FE5_2EFD_0CD8_0497, 0.0 }, // 0x1.52efd0cd80497p-1
+ { 26, 0hBFDA_05CC_7544_81D1, -0.0 }, // -0x1.a05cc754481d1p-2
+ { 27, 0h3FE1_F9EF_9347_45CB, 0.0 }, // 0x1.1f9ef934745cbp-1
+ { 28, 0h3FE8_C5DB_097F_7442, 0.0 }, // 0x1.8c5db097f7442p-1
+ { 29, 0hBFE5_B86E_A811_8A0E, -0.0 }, // -0x1.5b86ea8118a0ep-1
+ }
+
+ for d, i in data {
+ assert(i == d.i)
+ r = trunc_f64(d.v)
+ tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(%h) -> %h != %h", i, #procedure, d.v, r, d.e))
+ }
+
+ v = SNAN_F64
+ r = trunc_f64(v)
+ tc.expect(t, is_nan_f64(r), fmt.tprintf("%s(%f) -> %f != NaN", #procedure, v, r))
+
+ v = QNAN_F64
+ r = trunc_f64(v)
+ tc.expect(t, is_nan_f64(r), fmt.tprintf("%s(%f) -> %f != NaN", #procedure, v, r))
+}