diff --git a/core/math/math.odin b/core/math/math.odin index 4215a8075..a5d86cffb 100644 --- a/core/math/math.odin +++ b/core/math/math.odin @@ -203,7 +203,54 @@ pow10_f64 :: proc "contextless" (n: f64) -> f64 { return 0 } +pow2_f64 :: proc(#any_int exp: int) -> (res: f64) { + switch { + case exp >= -1022 && exp <= 1023: // Normal + return transmute(f64)(u64(exp + F64_BIAS) << F64_SHIFT) + case exp < -1075: // Underflow + return f64(0) + case exp == -1075: // Underflow + return 0h00000000_00000001 + case exp < -1022: // Denormal + x := u64(exp + (F64_SHIFT + 1) + F64_BIAS) << F64_SHIFT + return f64(1) / (1 << (F64_SHIFT + 1)) * transmute(f64)x + case exp > 1023: // Overflow, +Inf + return 0h7ff00000_00000000 + } + unreachable() +} +pow2_f32 :: proc(#any_int exp: int) -> (res: f32) { + switch { + case exp >= -126 && exp <= 127: // Normal + return transmute(f32)(u32(exp + F32_BIAS) << F32_SHIFT) + case exp < -151: // Underflow + return f32(0) + case exp < -126: // Denormal + x := u32(exp + (F32_SHIFT + 1) + F32_BIAS) << F32_SHIFT + return f32(1) / (1 << (F32_SHIFT + 1)) * transmute(f32)x + case exp > 127: // Overflow, +Inf + return 0h7f80_0000 + } + unreachable() +} + +pow2_f16 :: proc(#any_int exp: int) -> (res: f16) { + switch { + case exp >= -14 && exp <= 15: // Normal + return transmute(f16)(u16(exp + F16_BIAS) << F16_SHIFT) + case exp < -25: // Underflow + return 0h0000 + case exp == -25: // Underflow + return 0h0001 + case exp < -14: // Denormal + x := u16(exp + (F16_SHIFT + 1) + F16_BIAS) << F16_SHIFT + return f16(1) / (1 << (F16_SHIFT + 1)) * transmute(f16)x + case exp > 15: // Overflow, +Inf + return 0h7c00 + } + unreachable() +} @(require_results) ldexp_f64 :: proc "contextless" (val: f64, exp: int) -> f64 { @@ -2302,4 +2349,4 @@ 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) +QNAN_F64 :: f64(0h7FF8_0000_0000_0001) \ No newline at end of file diff --git a/tests/internal/Makefile b/tests/internal/Makefile index 898ba0517..00e46197b 100644 --- a/tests/internal/Makefile +++ b/tests/internal/Makefile @@ -1,9 +1,12 @@ ODIN=../../odin -all: rtti_test map_test +all: rtti_test map_test pow_test rtti_test: $(ODIN) run test_rtti.odin -file -vet -strict-style -o:minimal map_test: - $(ODIN) run test_map.odin -file -vet -strict-style -o:minimal \ No newline at end of file + $(ODIN) run test_map.odin -file -vet -strict-style -o:minimal + +pow_test: + $(ODIN) run test_pow.odin -file -vet -strict-style -o:minimal \ No newline at end of file diff --git a/tests/internal/build.bat b/tests/internal/build.bat index 1f40885bb..f289d17fa 100644 --- a/tests/internal/build.bat +++ b/tests/internal/build.bat @@ -2,4 +2,5 @@ set PATH_TO_ODIN==..\..\odin rem %PATH_TO_ODIN% run test_rtti.odin -file -vet -strict-style -o:minimal || exit /b %PATH_TO_ODIN% run test_map.odin -file -vet -strict-style -o:minimal || exit /b -rem -define:SEED=42 \ No newline at end of file +rem -define:SEED=42 +%PATH_TO_ODIN% run test_pow.odin -file -vet -strict-style -o:minimal || exit /b \ No newline at end of file diff --git a/tests/internal/test_pow.odin b/tests/internal/test_pow.odin new file mode 100644 index 000000000..b169e1361 --- /dev/null +++ b/tests/internal/test_pow.odin @@ -0,0 +1,67 @@ +package test_internal_math_pow + +import "core:fmt" +import "core:math" +import "core:os" +import "core:testing" + +@test +pow_test :: proc(t: ^testing.T) { + for exp in -2000..=2000 { + { + v1 := math.pow(2, f64(exp)) + v2 := math.pow2_f64(exp) + _v1 := transmute(u64)v1 + _v2 := transmute(u64)v2 + expect(t, _v1 == _v2, fmt.tprintf("Expected math.pow2_f64(%d) == math.pow(2, %d) (= %x), got %x", exp, exp, v1, v2)) + } + { + v1 := math.pow(2, f32(exp)) + v2 := math.pow2_f32(exp) + _v1 := transmute(u32)v1 + _v2 := transmute(u32)v2 + expect(t, _v1 == _v2, fmt.tprintf("Expected math.pow2_f32(%d) == math.pow(2, %d) (= %x), got %x", exp, exp, v1, v2)) + } + { + v1 := math.pow(2, f16(exp)) + v2 := math.pow2_f16(exp) + _v1 := transmute(u16)v1 + _v2 := transmute(u16)v2 + expect(t, _v1 == _v2, fmt.tprintf("Expected math.pow2_f16(%d) == math.pow(2, %d) (= %x), got %x", exp, exp, v1, v2)) + } + } +} + +// -------- -------- -------- -------- -------- -------- -------- -------- -------- -------- + +main :: proc() { + t := testing.T{} + + pow_test(&t) + + fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count) + if TEST_fail > 0 { + os.exit(1) + } +} + +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) + } +} \ No newline at end of file