Sfoglia il codice sorgente

Correcting libc pow bindings
Adding tests that libc pow(f) functions
- have two arguments
- behave as expected for simple inputs.

13419596 2 anni fa
parent
commit
4b4c2a2abd

+ 2 - 2
core/c/libc/complex.odin

@@ -49,8 +49,8 @@ foreign libc {
 	// 7.3.8 Power and absolute-value functions
 	cabs    :: proc(z: complex_double) -> complex_double ---
 	cabsf   :: proc(z: complex_float) -> complex_float ---
-	cpow    :: proc(z: complex_double) -> complex_double ---
-	cpowf   :: proc(z: complex_float) -> complex_float ---
+	cpow    :: proc(x, y: complex_double) -> complex_double ---
+	cpowf   :: proc(x, y: complex_float) -> complex_float ---
 	csqrt   :: proc(z: complex_double) -> complex_double ---
 	csqrtf  :: proc(z: complex_float) -> complex_float ---
 

+ 37 - 0
tests/core/c/libc/test_core_libc.odin

@@ -0,0 +1,37 @@
+package test_core_libc
+
+import "core:fmt"
+import "core:os"
+import "core:strings"
+import "core:testing"
+
+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] %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)
+	}
+}
+
+main :: proc() {
+	t := testing.T{}
+	test_libc_complex(&t)
+
+	fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count)
+	if TEST_fail > 0 {
+		os.exit(1)
+	}
+}

+ 91 - 0
tests/core/c/libc/test_core_libc_complex_pow.odin

@@ -0,0 +1,91 @@
+package test_core_libc
+
+import "core:testing"
+import "core:fmt"
+import "core:c/libc"
+
+reldiff :: proc(lhs, rhs: $T) -> f64  {
+	if lhs == rhs {
+		return 0.
+	}
+	amean := f64((abs(lhs)+abs(rhs)) / 2.)
+	adiff := f64(abs(lhs - rhs))
+	out := adiff / amean
+	return out
+}
+
+isclose :: proc(lhs, rhs: $T, rtol:f64 = 1e-12, atol:f64 = 1e-12) -> bool {
+	adiff := f64(abs(lhs - rhs))
+	if adiff < atol { 
+		return true
+	}
+	rdiff := reldiff(lhs, rhs)
+	if rdiff < rtol {
+		return true
+	}
+	fmt.printf("not close -- lhs:%v rhs:%v -- adiff:%e   rdiff:%e\n",lhs, rhs, adiff, rdiff)
+	return false
+}
+
+// declaring here so they can be used as function pointers
+
+libc_pow :: proc(x, y: libc.complex_double) -> libc.complex_double {
+	return libc.pow(x,y)
+}
+
+libc_powf :: proc(x, y: libc.complex_float) -> libc.complex_float {
+	return libc.pow(x,y)
+}
+
+@test
+test_libc_complex :: proc(t: ^testing.T) {
+	test_libc_pow_binding(t, libc.complex_double, f64, libc_pow, 1e-12, 1e-12)
+	// f32 needs more atol for comparing values close to zero
+	test_libc_pow_binding(t, libc.complex_float, f32, libc_powf, 1e-12, 1e-5)
+}
+
+@test
+test_libc_pow_binding :: proc(t: ^testing.T, $LIBC_COMPLEX:typeid, $F:typeid, pow: proc(LIBC_COMPLEX, LIBC_COMPLEX) -> LIBC_COMPLEX, 
+                              rtol: f64, atol: f64) {
+	// Tests that c/libc/pow(f) functions have two arguments and that the function works as expected for simple inputs
+	{
+		// tests 2^n
+		expected_real : F = 1./16.
+		expected_imag : F = 0.
+		complex_base := LIBC_COMPLEX(complex(F(2.), F(0.)))
+		for n in -4..=4 {
+			complex_power := LIBC_COMPLEX(complex(F(n), F(0.)))
+			result := pow(complex_base, complex_power) 
+			expect(t, isclose(expected_real, F(real(result)), rtol, atol), fmt.tprintf("ftype:%T, n:%v reldiff(%v, re(%v)) is greater than specified rtol:%e", F{}, n, expected_real, result, rtol))
+			expect(t, isclose(expected_imag, F(imag(result)), rtol, atol), fmt.tprintf("ftype:%T, n:%v reldiff(%v, im(%v)) is greater than specified rtol:%e", F{}, n, expected_imag, result, rtol))
+			expected_real *= 2
+		}
+	}
+	{
+		// tests (2i)^n
+		value : F = 1/16.
+		expected_real, expected_imag : F
+		complex_base := LIBC_COMPLEX(complex(F(0.), F(2.)))
+		for n in -4..=4 {
+			complex_power := LIBC_COMPLEX(complex(F(n), F(0.)))
+			result := pow(complex_base, complex_power) 
+			switch n%%4 {
+				case 0:
+					expected_real = value
+					expected_imag = 0.
+				case 1:
+					expected_real = 0.
+					expected_imag = value
+				case 2:
+					expected_real = -value
+					expected_imag = 0.
+				case 3:
+					expected_real = 0.
+					expected_imag = -value
+			}
+			expect(t, isclose(expected_real, F(real(result)), rtol, atol), fmt.tprintf("ftype:%T, n:%v reldiff(%v, re(%v)) is greater than specified rtol:%e", F{}, n, expected_real, result, rtol))
+			expect(t, isclose(expected_imag, F(imag(result)), rtol, atol), fmt.tprintf("ftype:%T, n:%v reldiff(%v, im(%v)) is greater than specified rtol:%e", F{}, n, expected_imag, result, rtol))
+			value *= 2
+		}
+	}
+}