| 1 | // Ported from musl, which is licensed under the MIT license: |
| 2 | // https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT |
| 3 | // |
| 4 | // https://git.musl-libc.org/cgit/musl/tree/src/math/logf.c |
| 5 | // https://git.musl-libc.org/cgit/musl/tree/src/math/log.c |
| 6 | |
| 7 | const std = @import("../std.zig"); |
| 8 | const math = std.math; |
| 9 | const expect = std.testing.expect; |
| 10 | |
| 11 | /// Returns the logarithm of x for the provided base. |
| 12 | pub fn log(comptime T: type, base: T, x: T) T { |
| 13 | if (base == 2) { |
| 14 | return math.log2(x); |
| 15 | } else if (base == 10) { |
| 16 | return math.log10(x); |
| 17 | } else if ((@typeInfo(T) == .float or @typeInfo(T) == .comptime_float) and base == math.e) { |
| 18 | return @log(x); |
| 19 | } |
| 20 | |
| 21 | const float_base = math.lossyCast(f64, base); |
| 22 | switch (@typeInfo(T)) { |
| 23 | .comptime_float => { |
| 24 | return @as(comptime_float, @log(@as(f64, x)) / @log(float_base)); |
| 25 | }, |
| 26 | |
| 27 | .comptime_int => { |
| 28 | return @as(comptime_int, math.log_int(comptime_int, base, x)); |
| 29 | }, |
| 30 | |
| 31 | .int => |IntType| switch (IntType.signedness) { |
| 32 | .signed => @compileError("log not implemented for signed integers"), |
| 33 | .unsigned => return @as(T, math.log_int(T, base, x)), |
| 34 | }, |
| 35 | |
| 36 | .float => { |
| 37 | switch (T) { |
| 38 | f32 => return @as(f32, @floatCast(@log(@as(f64, x)) / @log(float_base))), |
| 39 | f64 => return @log(x) / @log(float_base), |
| 40 | else => @compileError("log not implemented for " ++ @typeName(T)), |
| 41 | } |
| 42 | }, |
| 43 | |
| 44 | else => { |
| 45 | @compileError("log expects integer or float, found '" ++ @typeName(T) ++ "'"); |
| 46 | }, |
| 47 | } |
| 48 | } |
| 49 | |
| 50 | test "log integer" { |
| 51 | try expect(log(u8, 2, 0x1) == 0); |
| 52 | try expect(log(u8, 2, 0x2) == 1); |
| 53 | try expect(log(u16, 2, 0x72) == 6); |
| 54 | try expect(log(u32, 2, 0xFFFFFF) == 23); |
| 55 | try expect(log(u64, 2, 0x7FF0123456789ABC) == 62); |
| 56 | } |
| 57 | |
| 58 | test "log float" { |
| 59 | const epsilon = 0.000001; |
| 60 | |
| 61 | try expect(math.approxEqAbs(f32, log(f32, 6, 0.23947), -0.797723, epsilon)); |
| 62 | try expect(math.approxEqAbs(f32, log(f32, 89, 0.23947), -0.318432, epsilon)); |
| 63 | try expect(math.approxEqAbs(f64, log(f64, 123897, 12389216414), 1.981724596, epsilon)); |
| 64 | } |
| 65 | |
| 66 | test "log float_special" { |
| 67 | try expect(log(f32, 2, 0.2301974) == math.log2(@as(f32, 0.2301974))); |
| 68 | try expect(log(f32, 10, 0.2301974) == math.log10(@as(f32, 0.2301974))); |
| 69 | |
| 70 | try expect(log(f64, 2, 213.23019799993) == math.log2(@as(f64, 213.23019799993))); |
| 71 | try expect(log(f64, 10, 213.23019799993) == math.log10(@as(f64, 213.23019799993))); |
| 72 | } |