JUMBO vecmath completion update

This commit is contained in:
2026-01-04 15:43:10 +01:00
parent b09200b7ab
commit ed6391e97a
24 changed files with 3141 additions and 878 deletions

View File

@@ -1,11 +1,103 @@
const std = @import("std");
const vm = @import("root");
const vm = @import("root.zig");
/// The number of radians per one turn (τ).
pub const rad_per_turn = 1.0 * std.math.tau;
/// The number of degrees per one turn (360).
pub const deg_per_turn = 360.0;
/// The number of turns per one radian (1 / τ).
pub const turns_per_rad = 1.0 / std.math.tau;
/// The number of turns per one degree (1 / 360).
pub const turns_per_deg = 1.0 / 360.0;
/// Converts an angle in turns to radians. `@TypeOf(ang)` must be a float or
/// comptime number or a vector of floats.
pub fn turnsToRadians(ang: anytype) if (@TypeOf(ang) == comptime_int) comptime_float else @TypeOf(ang) {
const T = @TypeOf(ang);
switch (@typeInfo(T)) {
.float, .comptime_float, .comptime_int => return ang * rad_per_turn,
.vector => |V| if (@typeInfo(V.child) == .float) return ang * @as(T, @splat(rad_per_turn)),
else => {},
}
@compileError("Input must be float or a comptime number, or a vector of floats.");
}
test turnsToRadians {
try std.testing.expectEqual(0, turnsToRadians(@as(f32, 0)));
try std.testing.expectApproxEqAbs(0.25 * std.math.tau, turnsToRadians(@as(f32, 0.25)), 0x1p-10);
try std.testing.expectApproxEqAbs(0.5 * std.math.tau, turnsToRadians(@as(f32, 0.5)), 0x1p-10);
try std.testing.expectApproxEqAbs(0.75 * std.math.tau, turnsToRadians(@as(f32, 0.75)), 0x1p-10);
try std.testing.expectApproxEqAbs(std.math.tau, turnsToRadians(@as(f32, 1)), 0x1p-10);
}
/// Converts an angle in turns to degrees. `@TypeOf(ang)` must be a float or
/// comptime number or a vector of floats.
pub fn turnsToDegrees(ang: anytype) if (@TypeOf(ang) == comptime_int) comptime_float else @TypeOf(ang) {
const T = @TypeOf(ang);
switch (@typeInfo(T)) {
.float, .comptime_float, .comptime_int => return ang * deg_per_turn,
.vector => |V| if (@typeInfo(V.child) == .float) return ang * @as(T, @splat(deg_per_turn)),
else => {},
}
@compileError("Input must be float or a comptime number, or a vector of floats.");
}
test turnsToDegrees {
try std.testing.expectEqual(0, turnsToDegrees(@as(f32, 0)));
try std.testing.expectEqual(90, turnsToDegrees(@as(f32, 0.25)));
try std.testing.expectEqual(180, turnsToDegrees(@as(f32, 0.5)));
try std.testing.expectEqual(270, turnsToDegrees(@as(f32, 0.75)));
try std.testing.expectEqual(360, turnsToDegrees(@as(f32, 1)));
}
/// Converts an angle in radians to turns. `@TypeOf(ang)` must be a float or
/// comptime number or a vector of floats.
pub fn radiansToTurns(ang: anytype) if (@TypeOf(ang) == comptime_int) comptime_float else @TypeOf(ang) {
const T = @TypeOf(ang);
switch (@typeInfo(T)) {
.float, .comptime_float, .comptime_int => return ang * turns_per_rad,
.vector => |V| if (@typeInfo(V.child) == .float) return ang * @as(T, @splat(turns_per_rad)),
else => {},
}
@compileError("Input must be float or a comptime number, or a vector of floats.");
}
test radiansToTurns {
try std.testing.expectEqual(0, radiansToTurns(@as(f32, 0)));
try std.testing.expectApproxEqAbs(0.25, radiansToTurns(@as(f32, 0.25 * std.math.tau)), 0x1p-10);
try std.testing.expectApproxEqAbs(0.5, radiansToTurns(@as(f32, 0.5 * std.math.tau)), 0x1p-10);
try std.testing.expectApproxEqAbs(0.75, radiansToTurns(@as(f32, 0.75 * std.math.tau)), 0x1p-10);
try std.testing.expectApproxEqAbs(1, radiansToTurns(@as(f32, std.math.tau)), 0x1p-10);
}
/// Converts an angle in degrees to turns. `@TypeOf(ang)` must be a float or
/// comptime number or a vector of floats.
pub fn degreesToTurns(ang: anytype) if (@TypeOf(ang) == comptime_int) comptime_float else @TypeOf(ang) {
const T = @TypeOf(ang);
switch (@typeInfo(T)) {
.float, .comptime_float, .comptime_int => return ang / deg_per_turn,
.vector => |V| if (@typeInfo(V.child) == .float) return ang / @as(T, @splat(deg_per_turn)),
else => {},
}
@compileError("Input must be float or a comptime number, or a vector of floats.");
}
test degreesToTurns {
try std.testing.expectEqual(0, degreesToTurns(@as(f32, 0)));
try std.testing.expectEqual(0.25, degreesToTurns(@as(f32, 90)));
try std.testing.expectEqual(0.5, degreesToTurns(@as(f32, 180)));
try std.testing.expectEqual(0.75, degreesToTurns(@as(f32, 270)));
try std.testing.expectEqual(1, degreesToTurns(@as(f32, 360)));
}
pub fn cos(angle_turns: f32) f32 {
return cossin(angle_turns).re;
}
test "cos" {
test cos {
try std.testing.expectEqual(1, cos(-1));
try std.testing.expectEqual(0, cos(-0.75));
try std.testing.expectEqual(-1, cos(-0.5));
@@ -20,7 +112,7 @@ pub fn cos_x8(angle_turns: vm.f32x8) vm.f32x8 {
return cossin_x8(angle_turns).re;
}
test "cos_x8" {
test cos_x8 {
try std.testing.expectEqual(
.{ 1, 0, -1, 0, 1, 0, -1, 0 },
cos_x8(.{ -1, -0.75, -0.5, -0.25, 0, 0.25, 0.5, 0.75 }),
@@ -31,7 +123,7 @@ pub fn sin(angle_turns: f32) f32 {
return cossin(angle_turns).im;
}
test "sin" {
test sin {
try std.testing.expectEqual(0, sin(-1));
try std.testing.expectEqual(1, sin(-0.75));
try std.testing.expectEqual(0, sin(-0.5));
@@ -46,7 +138,7 @@ pub fn sin_x8(angle_turns: vm.f32x8) vm.f32x8 {
return cossin_x8(angle_turns).im;
}
test "sin_x8" {
test sin_x8 {
try std.testing.expectEqual(
.{ 0, 1, 0, -1, 0, 1, 0, -1 },
sin_x8(.{ -1, -0.75, -0.5, -0.25, 0, 0.25, 0.5, 0.75 }),
@@ -69,31 +161,31 @@ pub fn cossin(angle_turns: f32) vm.Complex {
const angle_04 = 4.0 * angle_01;
const quadrant: u32 = @intFromFloat(angle_04);
const quadrant_odd = (quadrant & 1) * 0xFFFFFFFF;
const sign_mask_cos = ((quadrant + 1) & 0b10) << 30;
const sign_mask_sin = (quadrant & 0b10) << 30;
const x = angle_04 - @floor(angle_04);
const x2 = x * x;
const c = ((term_cos_6 * x2 + term_cos_4) * x2 + term_cos_2) * x2 + term_cos_0;
const s = ((term_sin_5 * x2 + term_sin_3) * x2 + term_sin_1) * x;
const c: u32 = @bitCast(((term_cos_6 * x2 + term_cos_4) * x2 + term_cos_2) * x2 + term_cos_0);
const s: u32 = @bitCast(((term_sin_5 * x2 + term_sin_3) * x2 + term_sin_1) * x);
return switch (quadrant) {
0 => .init(c, s),
1 => .init(-s, c),
2 => .init(-c, -s),
3 => .init(s, -c),
else => unreachable,
};
const result_cos: f32 = @bitCast(((s & quadrant_odd) | (c & ~quadrant_odd)) ^ sign_mask_cos);
const result_sin: f32 = @bitCast(((c & quadrant_odd) | (s & ~quadrant_odd)) ^ sign_mask_sin);
return .init(result_cos, result_sin);
}
test "cossin" {
try std.testing.expectEqual(vm.Vector2.unit_x, cossin(-1));
try std.testing.expectEqual(vm.Vector2.unit_y, cossin(-0.75));
try std.testing.expectEqual(vm.Vector2.unit_nx, cossin(-0.5));
try std.testing.expectEqual(vm.Vector2.unit_ny, cossin(-0.25));
try std.testing.expectEqual(vm.Vector2.unit_x, cossin(0));
try std.testing.expectEqual(vm.Vector2.unit_y, cossin(0.25));
try std.testing.expectEqual(vm.Vector2.unit_nx, cossin(0.5));
try std.testing.expectEqual(vm.Vector2.unit_ny, cossin(0.75));
test cossin {
try std.testing.expectEqual(vm.Complex.initVector2(.unit_x), cossin(-1));
try std.testing.expectEqual(vm.Complex.initVector2(.unit_y), cossin(-0.75));
try std.testing.expectEqual(vm.Complex.initVector2(.unit_nx), cossin(-0.5));
try std.testing.expectEqual(vm.Complex.initVector2(.unit_ny), cossin(-0.25));
try std.testing.expectEqual(vm.Complex.initVector2(.unit_x), cossin(0));
try std.testing.expectEqual(vm.Complex.initVector2(.unit_y), cossin(0.25));
try std.testing.expectEqual(vm.Complex.initVector2(.unit_nx), cossin(0.5));
try std.testing.expectEqual(vm.Complex.initVector2(.unit_ny), cossin(0.75));
}
pub fn cossin_x8(angle_turns: vm.f32x8) vm.Complex_x8 {
@@ -119,39 +211,27 @@ pub fn cossin_x8(angle_turns: vm.f32x8) vm.Complex_x8 {
const x = angle_04 - @floor(angle_04);
const x2 = x * x;
const c = ((term_cos_6 * x2 + term_cos_4) * x2 + term_cos_2) * x2 + term_cos_0;
const s = ((term_sin_5 * x2 + term_sin_3) * x2 + term_sin_1) * x;
const c: vm.u32x8 = @bitCast(((term_cos_6 * x2 + term_cos_4) * x2 + term_cos_2) * x2 + term_cos_0);
const s: vm.u32x8 = @bitCast(((term_sin_5 * x2 + term_sin_3) * x2 + term_sin_1) * x);
var result_cos = @select(f32, quadrant_odd, s, c);
var result_sin = @select(f32, quadrant_odd, c, s);
result_cos = @bitCast(@as(vm.u32x8, @bitCast(result_cos)) ^ sign_mask_cos);
result_sin = @bitCast(@as(vm.u32x8, @bitCast(result_sin)) ^ sign_mask_sin);
const result_cos: vm.f32x8 = @bitCast(@select(u32, quadrant_odd, s, c) ^ sign_mask_cos);
const result_sin: vm.f32x8 = @bitCast(@select(u32, quadrant_odd, c, s) ^ sign_mask_sin);
return .init(result_cos, result_sin);
}
test "cossin_x8" {
test cossin_x8 {
try std.testing.expectEqual(
vm.Vector2x8.initArrayOfVectors(.{
vm.Vector2.unit_x,
vm.Vector2.unit_y,
vm.Vector2.unit_nx,
vm.Vector2.unit_ny,
vm.Vector2.unit_x,
vm.Vector2.unit_y,
vm.Vector2.unit_nx,
vm.Vector2.unit_ny,
}),
cossin_x8(.{
-1,
-0.75,
-0.5,
-0.25,
0,
0.25,
0.5,
0.75,
vm.Complex_x8.initArrayOfComplex(.{
.initVector2(.unit_x),
.initVector2(.unit_y),
.initVector2(.unit_nx),
.initVector2(.unit_ny),
.initVector2(.unit_x),
.initVector2(.unit_y),
.initVector2(.unit_nx),
.initVector2(.unit_ny),
}),
cossin_x8(.{ -1, -0.75, -0.5, -0.25, 0, 0.25, 0.5, 0.75 }),
);
}