From 4b4c2a2abd71dff2dc865df815f25318700e0929 Mon Sep 17 00:00:00 2001 From: 13419596 <13419596@hotmail.com> Date: Wed, 21 Sep 2022 17:15:28 -0500 Subject: [PATCH] Correcting libc pow bindings Adding tests that libc pow(f) functions - have two arguments - behave as expected for simple inputs. --- core/c/libc/complex.odin | 4 +- tests/core/c/libc/test_core_libc.odin | 37 ++++++++ .../c/libc/test_core_libc_complex_pow.odin | 91 +++++++++++++++++++ 3 files changed, 130 insertions(+), 2 deletions(-) create mode 100644 tests/core/c/libc/test_core_libc.odin create mode 100644 tests/core/c/libc/test_core_libc_complex_pow.odin diff --git a/core/c/libc/complex.odin b/core/c/libc/complex.odin index 22c422cce..7f2ca37ae 100644 --- a/core/c/libc/complex.odin +++ b/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 --- diff --git a/tests/core/c/libc/test_core_libc.odin b/tests/core/c/libc/test_core_libc.odin new file mode 100644 index 000000000..6ad37ac6d --- /dev/null +++ b/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) + } +} diff --git a/tests/core/c/libc/test_core_libc_complex_pow.odin b/tests/core/c/libc/test_core_libc_complex_pow.odin new file mode 100644 index 000000000..90928794c --- /dev/null +++ b/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 + } + } +} \ No newline at end of file