Repository: bitshifter/glam-rs
Pull request #64 (2020-06-28 01:45:28)

Split `Vec3` into 12 byte scalar implementation and 16 byte aligned SIMD implementation


Patch:
diff --git a/.travis.yml b/.travis.yml
index ac627f1..110cf8f 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -15,7 +15,6 @@ rust:
 
 env:
   - CARGO_FEATURES="mint rand serde debug-glam-assert transform-types"
-  - CARGO_FEATURES="mint rand serde packed-vec3 debug-glam-assert transform-types"
   - CARGO_FEATURES="mint rand serde scalar-math debug-glam-assert transform-types"
 
 matrix:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d22f77f..63a6005 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,14 @@ The format is based on [Keep a Changelog], and this project adheres to
 
 ## [Unreleased]
 
+### Changed
+* `Vec3` has been split into scalar `Vec3` and 16 byte aligned `Vec3A` types.
+  Only the `Vec3A` type currently uses SIMD optimizations.
+* `Vec3Mask` has been split into scalar `Vec3Mask` and 16 byte aligned
+  `Vec3AMask` types.
+* The `Mat3` type is using the scalar `Vec3` type for storage.
+* Removed the `packed-vec3` feature flag as it is now redundant.
+
 ## [0.8.7] - 2020-04-28
 
 ### Added
diff --git a/Cargo.toml b/Cargo.toml
index 7c5672d..913d3e3 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "glam"
-version = "0.8.7" # remember to update html_root_url
+version = "0.9.0-beta0" # remember to update html_root_url
 edition = "2018"
 authors = ["Cameron Hart <cameron.hart@gmail.com>"]
 description = "A simple and fast 3D math library for games and graphics"
@@ -27,9 +27,6 @@ debug-glam-assert = []
 # always enable additional glam checks
 glam-assert = []
 
-# opt out of Vec3 and Mat3 using SIMD (and thus being 16 byte aligned)
-packed-vec3 = []
-
 # this is primarily for testing the fallback implementation
 scalar-math = []
 
diff --git a/README.md b/README.md
index 1b2c6bc..860c2de 100644
--- a/README.md
+++ b/README.md
@@ -13,29 +13,23 @@ and the look and feel of the API has solidified.
 ## Features
 
 * Only single precision floating point (`f32`) arithmetic is supported
-* vectors: `Vec2`, `Vec3`, `Vec4`
+* vectors: `Vec2`, `Vec3`, `Vec3A` `Vec4`
 * square matrices: `Mat2`, `Mat3`, `Mat4`
 * a quaternion type: `Quat`
 
 ### SIMD
 
-The `Vec3`, `Vec4` and `Quat` types use SSE2 on x86/x86_64 architectures.
+The `Vec3A`, `Vec4` and `Quat` types use SSE2 on x86/x86_64 architectures.
 `Mat2`, `Mat3` and `Mat4` also use SSE2 for some functionality. Not everything
 has a SIMD implementation yet.
 
-Note that this does result in some wasted space in the case of `Vec3` and `Mat3`
-as the SIMD vector type is 16 bytes large and 16 byte aligned.
-
-It is possible to opt out of using SIMD types for Vec3 and Mat3 storage with the
-`packed-vec3` feature.
+Note that this does result in some wasted space in the case of `Vec3A` as the
+SIMD vector type is 16 bytes large and 16 byte aligned.
 
 `glam` outperforms similar Rust libraries such as [`cgmath`][cgmath],
 [`nalgebra-glm`][nalgebra-glm] and others for common operations as tested by the
 [`mathbench`][mathbench] project.
 
-If you are more concerned with size than speed you can build glam with the
-feature `scalar-math` enabled to disable SIMD usage.
-
 Due to the use of SIMD, vector elements may only be get and set via accessor
 methods, e.g. `Vec3::x()` and `Vec3::x_mut()` or `Vec3::set_x()`. If getting or
 setting more than one element it is more efficient to convert from tuples or
@@ -57,11 +51,10 @@ let [x, y, z]: [f32; 3] = v.into();
 
 ### Feature gates
 
-* `packed-vec3` - disable using SIMD types for `Vec3` and `Mat3` storage.  This
-  avoids wasting space due to 16 byte alignment at the cost of some performance.
 * `scalar-math` - compiles with SIMD support disabled
-* `glam-assert` - adds assertions which check the validity of parameters passed to
-  `glam` to help catch runtime errors
+* `debug-glam-assert` - adds assertions in debug builds which check the validity
+  of parameters passed to `glam` to help catch runtime errors
+* `glam-assert` - adds validation assertions to all builds
 
 ## Conventions
 
diff --git a/benches/vec3.rs b/benches/vec3.rs
index 4f26b23..9a7b19a 100644
--- a/benches/vec3.rs
+++ b/benches/vec3.rs
@@ -46,32 +46,32 @@ fn vec3_into_tuple(v: Vec3) -> (f32, f32, f32) {
 }
 
 bench_func!(
-    vec3_to_rgb,
-    "vec3 to rgb",
-    op => vec3_to_rgb_op,
-    from => random_vec3
-    );
+vec3_to_rgb,
+"vec3 to rgb",
+op => vec3_to_rgb_op,
+from => random_vec3
+);
 
 bench_func!(
-    vec3_to_array_accessors,
-    "vec3 into array slow",
-    op => vec3_accessors,
-    from => random_vec3
-    );
+vec3_to_array_accessors,
+"vec3 into array slow",
+op => vec3_accessors,
+from => random_vec3
+);
 
 bench_func!(
-    vec3_to_array_into,
-    "vec3 into array fast",
-    op => vec3_into_array,
-    from => random_vec3
-    );
+vec3_to_array_into,
+"vec3 into array fast",
+op => vec3_into_array,
+from => random_vec3
+);
 
 bench_func!(
-    vec3_to_tuple_into,
-    "vec3 into tuple fast",
-    op => vec3_into_tuple,
-    from => random_vec3
-    );
+vec3_to_tuple_into,
+"vec3 into tuple fast",
+op => vec3_into_tuple,
+from => random_vec3
+);
 
 euler!(vec3_euler, "vec3 euler", ty => Vec3, storage => Vec3, zero => Vec3::zero(), rand => random_vec3);
 
diff --git a/build.rs b/build.rs
index 0982fbc..5d23924 100644
--- a/build.rs
+++ b/build.rs
@@ -2,28 +2,23 @@ use std::env;
 
 fn main() {
     let force_scalar_math = env::var("CARGO_FEATURE_SCALAR_MATH").is_ok();
-    let force_packed_vec3 = env::var("CARGO_FEATURE_PACKED_VEC3").is_ok();
 
     let target_feature_sse2 = env::var("CARGO_CFG_TARGET_FEATURE")
         .map_or(false, |cfg| cfg.split(',').find(|&f| f == "sse2").is_some());
 
-    if target_feature_sse2 && !force_scalar_math && !force_packed_vec3 {
-        println!("cargo:rustc-cfg=vec3sse2");
+    if target_feature_sse2 && !force_scalar_math {
+        println!("cargo:rustc-cfg=vec3a_sse2");
     } else {
-        if !force_scalar_math && !force_packed_vec3 {
-            // simd not available but not explicitly disabled so maintain 16 byte alignment
-            println!("cargo:rustc-cfg=vec3f32_align16");
-        }
-        println!("cargo:rustc-cfg=vec3f32");
+        println!("cargo:rustc-cfg=vec3a_f32");
     }
 
     if target_feature_sse2 && !force_scalar_math {
-        println!("cargo:rustc-cfg=vec4sse2");
+        println!("cargo:rustc-cfg=vec4_sse2");
     } else {
         if !force_scalar_math {
             // simd not available but not explicitly disabled so maintain 16 byte alignment
-            println!("cargo:rustc-cfg=vec4f32_align16");
+            println!("cargo:rustc-cfg=vec4_f32_align16");
         }
-        println!("cargo:rustc-cfg=vec4f32");
+        println!("cargo:rustc-cfg=vec4_f32");
     }
 }
diff --git a/src/f32/funcs.rs b/src/f32/funcs.rs
index 90251bb..3fc3937 100644
--- a/src/f32/funcs.rs
+++ b/src/f32/funcs.rs
@@ -43,7 +43,7 @@ pub fn scalar_acos(value: f32) -> f32 {
     }
 }
 
-#[cfg(vec4sse2)]
+#[cfg(vec4_sse2)]
 pub(crate) mod sse2 {
     #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
     use crate::f32::x86_utils::UnionCast;
@@ -466,7 +466,7 @@ fn test_scalar_sin_cos() {
 }
 
 #[test]
-#[cfg(vec4sse2)]
+#[cfg(vec4_sse2)]
 fn test_sse2_m128_sin() {
     use crate::Vec4;
     use core::f32::consts::PI;
@@ -497,7 +497,7 @@ fn test_sse2_m128_sin() {
 // as the input angle drifts further from the bounds of PI.
 //
 // #[test]
-// #[cfg(vec4sse2)]
+// #[cfg(vec4_sse2)]
 // fn test_sse2_m128_sin2() {
 //     use crate::Vec4;
 
diff --git a/src/f32/glam_mint.rs b/src/f32/glam_mint.rs
index 350c595..0a2a669 100644
--- a/src/f32/glam_mint.rs
+++ b/src/f32/glam_mint.rs
@@ -1,4 +1,4 @@
-use super::{Mat2, Mat3, Mat4, Quat, Vec2, Vec3, Vec4};
+use super::{Mat2, Mat3, Mat4, Quat, Vec2, Vec3, Vec3A, Vec4};
 use mint;
 
 impl From<mint::Point2<f32>> for Vec2 {
@@ -20,6 +20,12 @@ impl From<mint::Point3<f32>> for Vec3 {
     }
 }
 
+impl From<mint::Point3<f32>> for Vec3A {
+    fn from(v: mint::Point3<f32>) -> Self {
+        Self::new(v.x, v.y, v.z)
+    }
+}
+
 impl From<Vec3> for mint::Point3<f32> {
     fn from(v: Vec3) -> Self {
         let (x, y, z) = v.into();
@@ -27,6 +33,13 @@ impl From<Vec3> for mint::Point3<f32> {
     }
 }
 
+impl From<Vec3A> for mint::Point3<f32> {
+    fn from(v: Vec3A) -> Self {
+        let (x, y, z) = v.into();
+        Self { x, y, z }
+    }
+}
+
 impl From<mint::Vector2<f32>> for Vec2 {
     fn from(v: mint::Vector2<f32>) -> Self {
         Self::new(v.x, v.y)
@@ -46,6 +59,12 @@ impl From<mint::Vector3<f32>> for Vec3 {
     }
 }
 
+impl From<mint::Vector3<f32>> for Vec3A {
+    fn from(v: mint::Vector3<f32>) -> Self {
+        Self::new(v.x, v.y, v.z)
+    }
+}
+
 impl From<Vec3> for mint::Vector3<f32> {
     fn from(v: Vec3) -> Self {
         let (x, y, z) = v.into();
@@ -53,6 +72,13 @@ impl From<Vec3> for mint::Vector3<f32> {
     }
 }
 
+impl From<Vec3A> for mint::Vector3<f32> {
+    fn from(v: Vec3A) -> Self {
+        let (x, y, z) = v.into();
+        Self { x, y, z }
+    }
+}
+
 impl From<mint::Vector4<f32>> for Vec4 {
     fn from(v: mint::Vector4<f32>) -> Self {
         Self::new(v.x, v.y, v.z, v.w)
@@ -195,7 +221,7 @@ mod test {
 
     #[test]
     fn test_point3() {
-        use crate::Vec3;
+        use crate::{Vec3, Vec3A};
         let m = mint::Point3 {
             x: 1.0,
             y: 2.0,
@@ -204,6 +230,9 @@ mod test {
         let g = Vec3::from(m);
         assert_eq!(g, Vec3::new(1.0, 2.0, 3.0));
         assert_eq!(m, g.into());
+        let g = Vec3A::from(m);
+        assert_eq!(g, Vec3A::new(1.0, 2.0, 3.0));
+        assert_eq!(m, g.into());
     }
 
     #[test]
@@ -217,7 +246,7 @@ mod test {
 
     #[test]
     fn test_vector3() {
-        use crate::Vec3;
+        use crate::{Vec3, Vec3A};
         let m = mint::Vector3 {
             x: 1.0,
             y: 2.0,
@@ -226,6 +255,9 @@ mod test {
         let g = Vec3::from(m);
         assert_eq!(g, Vec3::new(1.0, 2.0, 3.0));
         assert_eq!(m, g.into());
+        let g = Vec3A::from(m);
+        assert_eq!(g, Vec3A::new(1.0, 2.0, 3.0));
+        assert_eq!(m, g.into());
     }
 
     #[test]
diff --git a/src/f32/glam_rand.rs b/src/f32/glam_rand.rs
index f923850..f6efd23 100644
--- a/src/f32/glam_rand.rs
+++ b/src/f32/glam_rand.rs
@@ -1,4 +1,4 @@
-use super::{Mat2, Mat3, Mat4, Quat, Vec2, Vec3, Vec4};
+use super::{Mat2, Mat3, Mat4, Quat, Vec2, Vec3, Vec3A, Vec4};
 
 use rand::{
     distributions::{Distribution, Standard},
@@ -40,14 +40,21 @@ impl Distribution<Quat> for Standard {
 impl Distribution<Vec2> for Standard {
     #[inline]
     fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Vec2 {
-        rng.gen::<(f32, f32)>().into()
+        rng.gen::<[f32; 2]>().into()
     }
 }
 
 impl Distribution<Vec3> for Standard {
     #[inline]
     fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Vec3 {
-        rng.gen::<(f32, f32, f32)>().into()
+        rng.gen::<[f32; 3]>().into()
+    }
+}
+
+impl Distribution<Vec3A> for Standard {
+    #[inline]
+    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Vec3A {
+        rng.gen::<[f32; 3]>().into()
     }
 }
 
diff --git a/src/f32/glam_serde.rs b/src/f32/glam_serde.rs
index 8c08421..8ebe92d 100644
--- a/src/f32/glam_serde.rs
+++ b/src/f32/glam_serde.rs
@@ -1,4 +1,4 @@
-use crate::{Mat2, Mat3, Mat4, Quat, Vec2, Vec3, Vec4};
+use crate::{Mat2, Mat3, Mat4, Quat, Vec2, Vec3, Vec3A, Vec4};
 use core::fmt;
 use serde::{
     de::{self, Deserialize, Deserializer, SeqAccess, Visitor},
@@ -34,6 +34,22 @@ impl Serialize for Vec3 {
     }
 }
 
+#[cfg(feature = "serde")]
+impl Serialize for Vec3A {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: Serializer,
+    {
+        let (x, y, z) = (*self).into();
+        // 3 is the number of fields in the struct.
+        let mut state = serializer.serialize_tuple_struct("Vec3A", 3)?;
+        state.serialize_field(&x)?;
+        state.serialize_field(&y)?;
+        state.serialize_field(&z)?;
+        state.end()
+    }
+}
+
 #[cfg(feature = "serde")]
 impl Serialize for Vec4 {
     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
@@ -130,7 +146,6 @@ impl<'de> Deserialize<'de> for Vec2 {
         struct Vec2Visitor;
 
         // TODO: Not sure why this line is reported as uncovered
-        #[cfg_attr(tarpaulin, skip)]
         impl<'de> Visitor<'de> for Vec2Visitor {
             type Value = Vec2;
 
@@ -164,7 +179,6 @@ impl<'de> Deserialize<'de> for Vec3 {
         struct Vec3Visitor;
 
         // TODO: Not sure why this line is reported as uncovered
-        #[cfg_attr(tarpaulin, skip)]
         impl<'de> Visitor<'de> for Vec3Visitor {
             type Value = Vec3;
 
@@ -193,6 +207,42 @@ impl<'de> Deserialize<'de> for Vec3 {
     }
 }
 
+impl<'de> Deserialize<'de> for Vec3A {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: Deserializer<'de>,
+    {
+        struct Vec3AVisitor;
+
+        // TODO: Not sure why this line is reported as uncovered
+        impl<'de> Visitor<'de> for Vec3AVisitor {
+            type Value = Vec3A;
+
+            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+                formatter.write_str("struct Vec2")
+            }
+
+            fn visit_seq<V>(self, mut seq: V) -> Result<Vec3A, V::Error>
+            where
+                V: SeqAccess<'de>,
+            {
+                let x = seq
+                    .next_element()?
+                    .ok_or_else(|| de::Error::invalid_length(0, &self))?;
+                let y = seq
+                    .next_element()?
+                    .ok_or_else(|| de::Error::invalid_length(1, &self))?;
+                let z = seq
+                    .next_element()?
+                    .ok_or_else(|| de::Error::invalid_length(2, &self))?;
+                Ok(Vec3A::new(x, y, z))
+            }
+        }
+
+        deserializer.deserialize_tuple_struct("Vec3A", 3, Vec3AVisitor)
+    }
+}
+
 impl<'de> Deserialize<'de> for Vec4 {
     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
     where
@@ -201,7 +251,6 @@ impl<'de> Deserialize<'de> for Vec4 {
         struct Vec4Visitor;
 
         // TODO: Not sure why this line is reported as uncovered
-        #[cfg_attr(tarpaulin, skip)]
         impl<'de> Visitor<'de> for Vec4Visitor {
             type Value = Vec4;
 
@@ -241,7 +290,6 @@ impl<'de> Deserialize<'de> for Quat {
         struct QuatVisitor;
 
         // TODO: Not sure why this line is reported as uncovered
-        #[cfg_attr(tarpaulin, skip)]
         impl<'de> Visitor<'de> for QuatVisitor {
             type Value = Quat;
 
@@ -281,7 +329,6 @@ impl<'de> Deserialize<'de> for Mat2 {
         struct Mat2Visitor;
 
         // TODO: Not sure why this line is reported as uncovered
-        #[cfg_attr(tarpaulin, skip)]
         impl<'de> Visitor<'de> for Mat2Visitor {
             type Value = Mat2;
 
@@ -317,7 +364,6 @@ impl<'de> Deserialize<'de> for Mat3 {
         struct Mat3Visitor;
 
         // TODO: Not sure why this line is reported as uncovered
-        #[cfg_attr(tarpaulin, skip)]
         impl<'de> Visitor<'de> for Mat3Visitor {
             type Value = Mat3;
 
@@ -354,7 +400,6 @@ impl<'de> Deserialize<'de> for Mat4 {
         struct Mat4Visitor;
 
         // TODO: Not sure why this line is reported as uncovered
-        #[cfg_attr(tarpaulin, skip)]
         impl<'de> Visitor<'de> for Mat4Visitor {
             type Value = Mat4;
 
diff --git a/src/f32/mat2.rs b/src/f32/mat2.rs
index 4cccff1..fd53bab 100644
--- a/src/f32/mat2.rs
+++ b/src/f32/mat2.rs
@@ -1,7 +1,7 @@
 use super::{scalar_sin_cos, Vec2, Vec4};
-#[cfg(all(vec4sse2, target_arch = "x86",))]
+#[cfg(all(vec4_sse2, target_arch = "x86",))]
 use core::arch::x86::*;
-#[cfg(all(vec4sse2, target_arch = "x86_64",))]
+#[cfg(all(vec4_sse2, target_arch = "x86_64",))]
 use core::arch::x86_64::*;
 use core::{
     fmt,
@@ -172,14 +172,14 @@ impl Mat2 {
     /// Returns the transpose of `self`.
     #[inline]
     pub fn transpose(&self) -> Self {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         unsafe {
             let abcd = self.0.into();
             let acbd = _mm_shuffle_ps(abcd, abcd, 0b11_01_10_00);
             Self(acbd.into())
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             let (m00, m01, m10, m11) = self.0.into();
             Self(Vec4::new(m00, m10, m01, m11))
@@ -189,7 +189,7 @@ impl Mat2 {
     /// Returns the determinant of `self`.
     #[inline]
     pub fn determinant(&self) -> f32 {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         unsafe {
             let abcd = self.0.into();
             let dcba = _mm_shuffle_ps(abcd, abcd, 0b00_01_10_11);
@@ -198,7 +198,7 @@ impl Mat2 {
             _mm_cvtss_f32(det)
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             let (a, b, c, d) = self.0.into();
             a * d - b * c
@@ -210,7 +210,7 @@ impl Mat2 {
     /// If the matrix is not invertible the returned matrix will be invalid.
     #[inline]
     pub fn inverse(&self) -> Self {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         unsafe {
             let abcd = self.0.into();
             let dcba = _mm_shuffle_ps(abcd, abcd, 0b00_01_10_11);
@@ -222,7 +222,7 @@ impl Mat2 {
             Self(_mm_mul_ps(dbca, tmp).into())
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             let (a, b, c, d) = self.0.into();
             let det = a * d - b * c;
diff --git a/src/f32/mat3.rs b/src/f32/mat3.rs
index 7a22cd4..e898322 100644
--- a/src/f32/mat3.rs
+++ b/src/f32/mat3.rs
@@ -1,4 +1,4 @@
-use super::{scalar_sin_cos, Quat, Vec2, Vec3};
+use super::{scalar_sin_cos, Quat, Vec2, Vec3, Vec3A};
 use core::{
     fmt,
     ops::{Add, Mul, Sub},
@@ -306,33 +306,29 @@ impl Mat3 {
     /// Returns the transpose of `self`.
     #[inline]
     pub fn transpose(&self) -> Self {
-        #[cfg(vec3sse2)]
+        // #[cfg(vec3a_sse2)]
+        // {
+        //     #[cfg(target_arch = "x86")]
+        //     use core::arch::x86::*;
+        //     #[cfg(target_arch = "x86_64")]
+        //     use core::arch::x86_64::*;
+        //     unsafe {
+        //         let tmp0 = _mm_shuffle_ps(self.x_axis.0, self.y_axis.0, 0b01_00_01_00);
+        //         let tmp1 = _mm_shuffle_ps(self.x_axis.0, self.y_axis.0, 0b11_10_11_10);
+
+        //         Self {
+        //             x_axis: _mm_shuffle_ps(tmp0, self.z_axis.0, 0b00_00_10_00).into(),
+        //             y_axis: _mm_shuffle_ps(tmp0, self.z_axis.0, 0b01_01_11_01).into(),
+        //             z_axis: _mm_shuffle_ps(tmp1, self.z_axis.0, 0b10_10_10_00).into(),
+        //         }
+        //     }
+        // }
+        // #[cfg(vec3a_f32)]
         {
-            #[cfg(target_arch = "x86")]
-            use core::arch::x86::*;
-            #[cfg(target_arch = "x86_64")]
-            use core::arch::x86_64::*;
-            unsafe {
-                let tmp0 = _mm_shuffle_ps(self.x_axis.0, self.y_axis.0, 0b01_00_01_00);
-                let tmp1 = _mm_shuffle_ps(self.x_axis.0, self.y_axis.0, 0b11_10_11_10);
-
-                Self {
-                    x_axis: _mm_shuffle_ps(tmp0, self.z_axis.0, 0b00_00_10_00).into(),
-                    y_axis: _mm_shuffle_ps(tmp0, self.z_axis.0, 0b01_01_11_01).into(),
-                    z_axis: _mm_shuffle_ps(tmp1, self.z_axis.0, 0b10_10_10_00).into(),
-                }
-            }
-        }
-        #[cfg(vec3f32)]
-        {
-            let (m00, m01, m02) = self.x_axis.into();
-            let (m10, m11, m12) = self.y_axis.into();
-            let (m20, m21, m22) = self.z_axis.into();
-
             Self {
-                x_axis: Vec3::new(m00, m10, m20),
-                y_axis: Vec3::new(m01, m11, m21),
-                z_axis: Vec3::new(m02, m12, m22),
+                x_axis: Vec3::new(self.x_axis.0, self.y_axis.0, self.z_axis.0),
+                y_axis: Vec3::new(self.x_axis.1, self.y_axis.1, self.z_axis.1),
+                z_axis: Vec3::new(self.x_axis.2, self.y_axis.2, self.z_axis.2),
             }
         }
     }
@@ -360,10 +356,10 @@ impl Mat3 {
     /// Transforms a 3D vector.
     #[inline]
     pub fn mul_vec3(&self, other: Vec3) -> Vec3 {
-        let mut res = self.x_axis * other.dup_x();
-        res = self.y_axis.mul_add(other.dup_y(), res);
-        res = self.z_axis.mul_add(other.dup_z(), res);
-        res
+        let mut res = Vec3A::from(self.x_axis) * Vec3A::splat(other.x());
+        res = Vec3A::from(self.y_axis).mul_add(Vec3A::splat(other.y()), res);
+        res = Vec3A::from(self.z_axis).mul_add(Vec3A::splat(other.z()), res);
+        Vec3::from(res)
     }
 
     /// Multiplies two 3x3 matrices.
diff --git a/src/f32/mat4.rs b/src/f32/mat4.rs
index a5439ab..f4b3782 100644
--- a/src/f32/mat4.rs
+++ b/src/f32/mat4.rs
@@ -1,7 +1,7 @@
-use super::{scalar_sin_cos, Mat3, Quat, Vec3, Vec4};
-#[cfg(all(vec4sse2, target_arch = "x86"))]
+use super::{scalar_sin_cos, Mat3, Quat, Vec3, Vec3A, Vec4};
+#[cfg(all(vec4_sse2, target_arch = "x86"))]
 use core::arch::x86::*;
-#[cfg(all(vec4sse2, target_arch = "x86_64"))]
+#[cfg(all(vec4_sse2, target_arch = "x86_64"))]
 use core::arch::x86_64::*;
 use core::{
     fmt,
@@ -183,22 +183,23 @@ impl Mat4 {
         let det = self.determinant();
         glam_assert!(det != 0.0);
 
-        let scale = Vec3::new(
+        let scale = Vec3A::new(
             self.x_axis.length() * det.signum(),
             self.y_axis.length(),
             self.z_axis.length(),
         );
-        glam_assert!(scale.cmpne(Vec3::zero()).all());
+        glam_assert!(scale.cmpne(Vec3A::zero()).all());
 
         let inv_scale = scale.reciprocal();
 
         let rotation = Quat::from_rotation_mat3(&Mat3::from_cols(
-            self.x_axis().truncate() * inv_scale.dup_x(),
-            self.y_axis().truncate() * inv_scale.dup_y(),
-            self.z_axis().truncate() * inv_scale.dup_z(),
+            Vec3::from(self.x_axis().truncate() * inv_scale.dup_x()),
+            Vec3::from(self.y_axis().truncate() * inv_scale.dup_y()),
+            Vec3::from(self.z_axis().truncate() * inv_scale.dup_z()),
         ));
 
-        let translation = self.w_axis.truncate();
+        let translation = Vec3::from(self.w_axis.truncate());
+        let scale = Vec3::from(scale);
 
         (scale, rotation, translation)
     }
@@ -401,7 +402,7 @@ impl Mat4 {
     /// Returns the transpose of `self`.
     #[inline]
     pub fn transpose(&self) -> Self {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         unsafe {
             // sse2 implementation based off DirectXMath XMMatrixInverse (MIT License)
             let tmp0 = _mm_shuffle_ps(self.x_axis.0, self.y_axis.0, 0b01_00_01_00);
@@ -417,7 +418,7 @@ impl Mat4 {
             }
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             let (m00, m01, m02, m03) = self.x_axis.into();
             let (m10, m11, m12, m13) = self.y_axis.into();
@@ -807,7 +808,7 @@ impl Mat4 {
         // // other w = 1
         // res = self.w_axis.truncate() + res;
         // res
-        self.mul_vec4(other.extend(1.0)).truncate()
+        Vec3::from(self.mul_vec4(other.extend(1.0)).truncate())
     }
 
     /// Transforms the give `Vec3` as 3D vector.
@@ -822,7 +823,7 @@ impl Mat4 {
         // res = self.z_axis.truncate().mul_add(other.dup_z(), res);
         // // other w = 0
         // res
-        self.mul_vec4(other.extend(0.0)).truncate()
+        Vec3::from(self.mul_vec4(other.extend(0.0)).truncate())
     }
 
     /// Returns true if the absolute difference of all elements between `self`
diff --git a/src/f32/mod.rs b/src/f32/mod.rs
index a998651..596e8b9 100644
--- a/src/f32/mod.rs
+++ b/src/f32/mod.rs
@@ -9,6 +9,8 @@ mod vec2;
 mod vec2_mask;
 mod vec3;
 mod vec3_mask;
+mod vec3a;
+mod vec3a_mask;
 mod vec4;
 mod vec4_mask;
 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
@@ -25,6 +27,8 @@ pub use vec2::*;
 pub use vec2_mask::*;
 pub use vec3::*;
 pub use vec3_mask::*;
+pub use vec3a::*;
+pub use vec3a_mask::*;
 pub use vec4::*;
 pub use vec4_mask::*;
 
diff --git a/src/f32/quat.rs b/src/f32/quat.rs
index 15fa7b8..2967f3f 100644
--- a/src/f32/quat.rs
+++ b/src/f32/quat.rs
@@ -1,7 +1,7 @@
-use super::{scalar_acos, scalar_sin_cos, Mat3, Mat4, Vec3, Vec4};
-#[cfg(all(vec4sse2, target_arch = "x86",))]
+use super::{scalar_acos, scalar_sin_cos, Mat3, Mat4, Vec3, Vec3A, Vec4};
+#[cfg(all(vec4_sse2, target_arch = "x86",))]
 use core::arch::x86::*;
-#[cfg(all(vec4sse2, target_arch = "x86_64",))]
+#[cfg(all(vec4_sse2, target_arch = "x86_64",))]
 use core::arch::x86_64::*;
 use core::{
     cmp::Ordering,
@@ -191,9 +191,9 @@ impl Quat {
     #[inline]
     pub fn from_rotation_mat4(mat: &Mat4) -> Self {
         Self::from_rotation_axes(
-            mat.x_axis().truncate(),
-            mat.y_axis().truncate(),
-            mat.z_axis().truncate(),
+            Vec3::from(mat.x_axis().truncate()),
+            Vec3::from(mat.y_axis().truncate()),
+            Vec3::from(mat.z_axis().truncate()),
         )
     }
 
@@ -216,7 +216,7 @@ impl Quat {
     /// conjugate is also the inverse.
     #[inline]
     pub fn conjugate(self) -> Self {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         unsafe {
             Self(Vec4(_mm_xor_ps(
                 (self.0).0,
@@ -224,7 +224,7 @@ impl Quat {
             )))
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             Self::from_xyzw(-(self.0).0, -(self.0).1, -(self.0).2, (self.0).3)
         }
@@ -322,7 +322,7 @@ impl Quat {
         glam_assert!(self.is_normalized());
         glam_assert!(end.is_normalized());
 
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         unsafe {
             let start = self.0;
             let end = end.0;
@@ -340,7 +340,7 @@ impl Quat {
             Self(interpolated.normalize())
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             let start = self.0;
             let end = end.0;
@@ -378,7 +378,7 @@ impl Quat {
             // assumes lerp returns a normalized quaternion
             self.lerp(end, s)
         } else {
-            #[cfg(vec4f32)]
+            #[cfg(vec4_f32)]
             {
                 // assumes scalar_acos clamps the input to [-1.0, 1.0]
                 let theta = crate::f32::funcs::scalar_acos(dot);
@@ -389,7 +389,7 @@ impl Quat {
                 Quat((self.0 * scale1 + end.0 * scale2) * theta_sin.recip())
             }
 
-            #[cfg(vec4sse2)]
+            #[cfg(vec4_sse2)]
             {
                 // assumes scalar_acos clamps the input to [-1.0, 1.0]
                 let theta = crate::f32::funcs::scalar_acos(dot);
@@ -416,24 +416,27 @@ impl Quat {
 
     #[inline]
     /// Multiplies a quaternion and a 3D vector, rotating it.
-    pub fn mul_vec3(self, other: Vec3) -> Vec3 {
+    pub fn mul_vec3(self, other: Vec3A) -> Vec3A {
         glam_assert!(self.is_normalized());
 
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         {
             let w = self.0.dup_w().truncate();
-            let two = Vec3::splat(2.0);
+            let two = Vec3A::splat(2.0);
             let b = self.0.truncate();
             let b2 = b.dot_as_vec3(b);
             other * (w * w - b2) + b * (other.dot_as_vec3(b) * two) + b.cross(other) * (w * two)
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
+            let other = Vec3::from(other);
             let w = self.0.w();
-            let b = self.0.truncate();
+            let b = Vec3::from(self.0.truncate());
             let b2 = b.dot(b);
-            other * (w * w - b2) + b * (other.dot(b) * 2.0) + b.cross(other) * (w * 2.0)
+            Vec3A::from(
+                other * (w * w - b2) + b * (other.dot(b) * 2.0) + b.cross(other) * (w * 2.0),
+            )
         }
     }
 
@@ -444,7 +447,7 @@ impl Quat {
         glam_assert!(self.is_normalized());
         glam_assert!(other.is_normalized());
 
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         unsafe {
             // from rtm quat_mul
             let lhs = self.0.into();
@@ -480,7 +483,7 @@ impl Quat {
             Self(Vec4(_mm_add_ps(result0, result1)))
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             let (x0, y0, z0, w0) = self.0.into();
             let (x1, y1, z1, w1) = other.0.into();
@@ -519,12 +522,12 @@ impl Quat {
 
 impl fmt::Debug for Quat {
     fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         {
             fmt.debug_tuple("Quat").field(&(self.0).0).finish()
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             fmt.debug_tuple("Quat")
                 .field(&self.0.x())
@@ -561,7 +564,15 @@ impl MulAssign<Quat> for Quat {
 impl Mul<Vec3> for Quat {
     type Output = Vec3;
     #[inline]
-    fn mul(self, other: Vec3) -> Vec3 {
+    fn mul(self, other: Vec3) -> Self::Output {
+        Vec3::from(self.mul_vec3(Vec3A::from(other)))
+    }
+}
+
+impl Mul<Vec3A> for Quat {
+    type Output = Vec3A;
+    #[inline]
+    fn mul(self, other: Vec3A) -> Self::Output {
         self.mul_vec3(other)
     }
 }
@@ -651,17 +662,16 @@ impl From<Quat> for [f32; 4] {
     }
 }
 
-#[cfg(vec4sse2)]
+#[cfg(vec4_sse2)]
 impl From<Quat> for __m128 {
     // TODO: write test
-    #[cfg_attr(tarpaulin, skip)]
     #[inline]
     fn from(q: Quat) -> Self {
         (q.0).0
     }
 }
 
-#[cfg(vec4sse2)]
+#[cfg(vec4_sse2)]
 impl From<__m128> for Quat {
     #[inline]
     fn from(t: __m128) -> Self {
diff --git a/src/f32/transform.rs b/src/f32/transform.rs
index 3bd17e0..129c3ee 100644
--- a/src/f32/transform.rs
+++ b/src/f32/transform.rs
@@ -1,4 +1,4 @@
-use super::{Mat4, Quat, Vec3};
+use super::{Mat4, Quat, Vec3, Vec3A};
 use core::ops::Mul;
 
 #[cfg(feature = "rand")]
@@ -125,10 +125,12 @@ impl TransformSRT {
 #[inline]
 fn mul_srt_srt(lhs: &TransformSRT, rhs: &TransformSRT) -> TransformSRT {
     // from rtm qvv_mul
-    let min_scale = lhs.scale.min(rhs.scale);
-    let scale = lhs.scale * rhs.scale;
+    let lhs_scale = Vec3A::from(lhs.scale);
+    let rhs_scale = Vec3A::from(rhs.scale);
+    let min_scale = lhs_scale.min(rhs_scale);
+    let scale = lhs_scale * rhs_scale;
 
-    if min_scale.cmplt(Vec3::zero()).any() {
+    if min_scale.cmplt(Vec3A::zero()).any() {
         // If negative scale, we go through a matrix
         let lhs_mtx =
             Mat4::from_scale_rotation_translation(lhs.scale, lhs.rotation, lhs.translation);
@@ -144,16 +146,21 @@ fn mul_srt_srt(lhs: &TransformSRT, rhs: &TransformSRT) -> TransformSRT {
         result_mtx
             .set_z_axis((result_mtx.z_axis().truncate().normalize() * sign.dup_z()).extend(0.0));
 
+        let scale = Vec3::from(scale);
         let rotation = Quat::from_rotation_mat4(&result_mtx);
-        let translation = result_mtx.w_axis().truncate();
+        let translation = Vec3::from(result_mtx.w_axis().truncate());
         TransformSRT {
             scale,
             rotation,
             translation,
         }
     } else {
+        let scale = Vec3::from(scale);
         let rotation = lhs.rotation * rhs.rotation;
-        let translation = (rhs.rotation * (lhs.translation * rhs.scale)) + rhs.translation;
+        let translation = Vec3::from(
+            (rhs.rotation * (Vec3A::from(lhs.translation) * rhs_scale))
+                + Vec3A::from(rhs.translation),
+        );
         TransformSRT {
             scale,
             rotation,
diff --git a/src/f32/vec2.rs b/src/f32/vec2.rs
index 809a03f..ffe6395 100644
--- a/src/f32/vec2.rs
+++ b/src/f32/vec2.rs
@@ -371,6 +371,14 @@ impl DivAssign<f32> for Vec2 {
     }
 }
 
+impl Div<Vec2> for f32 {
+    type Output = Vec2;
+    #[inline]
+    fn div(self, other: Vec2) -> Vec2 {
+        Vec2(self / other.0, self / other.1)
+    }
+}
+
 impl Mul<Vec2> for Vec2 {
     type Output = Self;
     #[inline]
diff --git a/src/f32/vec3.rs b/src/f32/vec3.rs
index 53ca3e5..171d50c 100644
--- a/src/f32/vec3.rs
+++ b/src/f32/vec3.rs
@@ -1,91 +1,11 @@
-use super::{Vec2, Vec3Mask, Vec4};
+use super::{Vec2, Vec3A, Vec3Mask, Vec4};
 use core::{fmt, ops::*};
 
-#[cfg(all(vec3sse2, target_arch = "x86"))]
-use core::arch::x86::*;
-#[cfg(all(vec3sse2, target_arch = "x86_64"))]
-use core::arch::x86_64::*;
-
-#[cfg(vec3sse2)]
-use core::{cmp::Ordering, f32, mem::MaybeUninit};
-
-#[cfg(vec3sse2)]
-use crate::{
-    f32::{X_AXIS, Y_AXIS, Z_AXIS},
-    Align16,
-};
-
 /// A 3-dimensional vector.
-///
-/// This type is 16 byte aligned and thus contains 4 bytes padding.
-#[cfg(vec3sse2)]
-#[derive(Clone, Copy, Debug)]
-#[repr(C)]
-pub struct Vec3(pub(crate) __m128);
-
-/// A 3-dimensional vector.
-#[cfg(vec3f32)]
 #[derive(Clone, Copy, PartialEq, PartialOrd, Debug, Default)]
-// if compiling with simd enabled assume alignment needs to match the simd type
-#[cfg_attr(vec3f32_align16, repr(align(16)))]
 #[repr(C)]
 pub struct Vec3(pub(crate) f32, pub(crate) f32, pub(crate) f32);
 
-#[cfg(vec3sse2)]
-impl Vec3 {
-    /// Calculates the Vec3 dot product and returns answer in x lane of __m128.
-    #[inline]
-    unsafe fn dot_as_m128(self, other: Self) -> __m128 {
-        let x2_y2_z2_w2 = _mm_mul_ps(self.0, other.0);
-        let y2_0_0_0 = _mm_shuffle_ps(x2_y2_z2_w2, x2_y2_z2_w2, 0b00_00_00_01);
-        let z2_0_0_0 = _mm_shuffle_ps(x2_y2_z2_w2, x2_y2_z2_w2, 0b00_00_00_10);
-        let x2y2_0_0_0 = _mm_add_ss(x2_y2_z2_w2, y2_0_0_0);
-        _mm_add_ss(x2y2_0_0_0, z2_0_0_0)
-    }
-}
-
-#[cfg(vec3sse2)]
-impl Default for Vec3 {
-    #[inline]
-    fn default() -> Self {
-        Vec3::zero()
-    }
-}
-
-#[cfg(vec3sse2)]
-impl PartialEq for Vec3 {
-    #[inline]
-    fn eq(&self, other: &Self) -> bool {
-        self.cmpeq(*other).all()
-    }
-}
-
-#[cfg(vec3sse2)]
-impl PartialOrd for Vec3 {
-    #[inline]
-    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
-        self.as_ref().partial_cmp(other.as_ref())
-    }
-}
-
-#[cfg(vec3sse2)]
-impl From<Vec3> for __m128 {
-    // TODO: write test
-    #[cfg_attr(tarpaulin, skip)]
-    #[inline]
-    fn from(t: Vec3) -> Self {
-        t.0
-    }
-}
-
-#[cfg(vec3sse2)]
-impl From<__m128> for Vec3 {
-    #[inline]
-    fn from(t: __m128) -> Self {
-        Self(t)
-    }
-}
-
 #[inline]
 pub fn vec3(x: f32, y: f32, z: f32) -> Vec3 {
     Vec3::new(x, y, z)
@@ -95,375 +15,161 @@ impl Vec3 {
     /// Creates a new `Vec3`.
     #[inline]
     pub fn new(x: f32, y: f32, z: f32) -> Self {
-        #[cfg(vec3sse2)]
-        unsafe {
-            Self(_mm_set_ps(z, z, y, x))
-        }
-
-        #[cfg(vec3f32)]
-        {
-            Self(x, y, z)
-        }
+        Self(x, y, z)
     }
 
     /// Creates a new `Vec3` with all elements set to `0.0`.
     #[inline]
     pub fn zero() -> Self {
-        #[cfg(vec3sse2)]
-        unsafe {
-            Self(_mm_setzero_ps())
-        }
-
-        #[cfg(vec3f32)]
-        {
-            Self(0.0, 0.0, 0.0)
-        }
+        Self(0.0, 0.0, 0.0)
     }
 
     /// Creates a new `Vec3` with all elements set to `1.0`.
     #[inline]
     pub fn one() -> Self {
-        #[cfg(vec3sse2)]
-        unsafe {
-            Self(_mm_set1_ps(1.0))
-        }
-
-        #[cfg(vec3f32)]
-        {
-            Self(1.0, 1.0, 1.0)
-        }
+        Self(1.0, 1.0, 1.0)
     }
 
     /// Creates a new `Vec3` with values `[x: 1.0, y: 0.0, z: 0.0]`.
     #[inline]
     pub fn unit_x() -> Self {
-        #[cfg(vec3sse2)]
-        unsafe {
-            Self(_mm_load_ps(X_AXIS.0.as_ptr()))
-        }
-
-        #[cfg(vec3f32)]
-        {
-            Self(1.0, 0.0, 0.0)
-        }
+        Self(1.0, 0.0, 0.0)
     }
 
     /// Creates a new `Vec3` with values `[x: 0.0, y: 1.0, z: 0.0]`.
     #[inline]
     pub fn unit_y() -> Self {
-        #[cfg(vec3sse2)]
-        unsafe {
-            Self(_mm_load_ps(Y_AXIS.0.as_ptr()))
-        }
-
-        #[cfg(vec3f32)]
-        {
-            Self(0.0, 1.0, 0.0)
-        }
+        Self(0.0, 1.0, 0.0)
     }
 
     /// Creates a new `Vec3` with values `[x: 0.0, y: 0.0, z: 1.0]`.
     #[inline]
     pub fn unit_z() -> Self {
-        #[cfg(vec3sse2)]
-        unsafe {
-            Self(_mm_load_ps(Z_AXIS.0.as_ptr()))
-        }
-
-        #[cfg(vec3f32)]
-        {
-            Self(0.0, 0.0, 1.0)
-        }
+        Self(0.0, 0.0, 1.0)
     }
 
     /// Creates a new `Vec3` with all elements set to `v`.
     #[inline]
     pub fn splat(v: f32) -> Self {
-        #[cfg(vec3sse2)]
-        unsafe {
-            Self(_mm_set_ps1(v))
-        }
-
-        #[cfg(vec3f32)]
-        {
-            Self(v, v, v)
-        }
+        Self(v, v, v)
     }
 
     /// Creates a new `Vec4` from `self` and the given `w` value.
     #[inline]
     pub fn extend(self, w: f32) -> Vec4 {
-        #[cfg(vec3sse2)]
-        {
-            let mut temp: Vec4 = self.0.into();
-            temp.set_w(w);
-            temp
-        }
-
-        #[cfg(vec3f32)]
-        {
-            Vec4::new(self.0, self.1, self.2, w)
-        }
+        Vec4::new(self.0, self.1, self.2, w)
     }
 
     /// Creates a `Vec2` from the first three elements of `self`,
     /// removing `z`.
     #[inline]
     pub fn truncate(self) -> Vec2 {
-        #[cfg(vec3sse2)]
-        {
-            let (x, y, _) = self.into();
-            Vec2::new(x, y)
-        }
-
-        #[cfg(vec3f32)]
-        {
-            Vec2::new(self.0, self.1)
-        }
+        Vec2::new(self.0, self.1)
     }
 
     /// Returns element `x`.
     #[inline]
     pub fn x(self) -> f32 {
-        #[cfg(vec3sse2)]
-        unsafe {
-            _mm_cvtss_f32(self.0)
-        }
-
-        #[cfg(vec3f32)]
-        {
-            self.0
-        }
+        self.0
     }
 
     /// Returns element `y`.
     #[inline]
     pub fn y(self) -> f32 {
-        #[cfg(vec3sse2)]
-        unsafe {
-            _mm_cvtss_f32(_mm_shuffle_ps(self.0, self.0, 0b01_01_01_01))
-        }
-
-        #[cfg(vec3f32)]
-        {
-            self.1
-        }
+        self.1
     }
 
     /// Returns element `z`.
     #[inline]
     pub fn z(self) -> f32 {
-        #[cfg(vec3sse2)]
-        unsafe {
-            _mm_cvtss_f32(_mm_shuffle_ps(self.0, self.0, 0b10_10_10_10))
-        }
-
-        #[cfg(vec3f32)]
-        {
-            self.2
-        }
+        self.2
     }
 
     /// Returns a mutable reference to element `x`.
     #[inline]
     pub fn x_mut(&mut self) -> &mut f32 {
-        #[cfg(vec3sse2)]
-        unsafe {
-            &mut *(self as *mut Self as *mut f32)
-        }
-
-        #[cfg(vec3f32)]
-        {
-            &mut self.0
-        }
+        &mut self.0
     }
 
     /// Returns a mutable reference to element `y`.
     #[inline]
     pub fn y_mut(&mut self) -> &mut f32 {
-        #[cfg(vec3sse2)]
-        unsafe {
-            &mut *(self as *mut Self as *mut f32).offset(1)
-        }
-
-        #[cfg(vec3f32)]
-        {
-            &mut self.1
-        }
+        &mut self.1
     }
 
     /// Returns a mutable reference to element `z`.
     #[inline]
     pub fn z_mut(&mut self) -> &mut f32 {
-        #[cfg(vec3sse2)]
-        unsafe {
-            &mut *(self as *mut Self as *mut f32).offset(2)
-        }
-
-        #[cfg(vec3f32)]
-        {
-            &mut self.2
-        }
+        &mut self.2
     }
 
     /// Sets element `x`.
     #[inline]
     pub fn set_x(&mut self, x: f32) {
-        #[cfg(vec3sse2)]
-        unsafe {
-            self.0 = _mm_move_ss(self.0, _mm_set_ss(x));
-        }
-
-        #[cfg(vec3f32)]
-        {
-            self.0 = x;
-        }
+        self.0 = x;
     }
 
     /// Sets element `y`.
     #[inline]
     pub fn set_y(&mut self, y: f32) {
-        #[cfg(vec3sse2)]
-        unsafe {
-            let mut t = _mm_move_ss(self.0, _mm_set_ss(y));
-            t = _mm_shuffle_ps(t, t, 0b11_10_00_00);
-            self.0 = _mm_move_ss(t, self.0);
-        }
-
-        #[cfg(vec3f32)]
-        {
-            self.1 = y;
-        }
+        self.1 = y;
     }
 
     /// Sets element `z`.
     #[inline]
     pub fn set_z(&mut self, z: f32) {
-        #[cfg(vec3sse2)]
-        unsafe {
-            let mut t = _mm_move_ss(self.0, _mm_set_ss(z));
-            t = _mm_shuffle_ps(t, t, 0b11_00_01_00);
-            self.0 = _mm_move_ss(t, self.0);
-        }
-
-        #[cfg(vec3f32)]
-        {
-            self.2 = z;
-        }
+        self.2 = z;
     }
 
     /// Returns a `Vec3` with all elements set to the value of element `x`.
     #[inline]
+    #[allow(dead_code)]
     pub(crate) fn dup_x(self) -> Self {
-        #[cfg(vec3sse2)]
-        unsafe {
-            Self(_mm_shuffle_ps(self.0, self.0, 0b00_00_00_00))
-        }
-
-        #[cfg(vec3f32)]
-        {
-            Self(self.0, self.0, self.0)
-        }
+        Self(self.0, self.0, self.0)
     }
 
     /// Returns a `Vec3` with all elements set to the value of element `y`.
     #[inline]
+    #[allow(dead_code)]
     pub(crate) fn dup_y(self) -> Self {
-        #[cfg(vec3sse2)]
-        unsafe {
-            Self(_mm_shuffle_ps(self.0, self.0, 0b01_01_01_01))
-        }
-
-        #[cfg(vec3f32)]
-        {
-            Self(self.1, self.1, self.1)
-        }
+        Self(self.1, self.1, self.1)
     }
 
     /// Returns a `Vec3` with all elements set to the value of element `z`.
     #[inline]
+    #[allow(dead_code)]
     pub(crate) fn dup_z(self) -> Self {
-        #[cfg(vec3sse2)]
-        unsafe {
-            Self(_mm_shuffle_ps(self.0, self.0, 0b10_10_10_10))
-        }
-
-        #[cfg(vec3f32)]
-        {
-            Self(self.2, self.2, self.2)
-        }
+        Self(self.2, self.2, self.2)
     }
 
     /// Computes the dot product of `self` and `other`.
     #[inline]
     pub fn dot(self, other: Self) -> f32 {
-        #[cfg(vec3sse2)]
-        unsafe {
-            _mm_cvtss_f32(self.dot_as_m128(other))
-        }
-
-        #[cfg(vec3f32)]
-        {
-            (self.0 * other.0) + (self.1 * other.1) + (self.2 * other.2)
-        }
+        (self.0 * other.0) + (self.1 * other.1) + (self.2 * other.2)
     }
 
     /// Returns Vec3 dot in all lanes of Vec3
     #[inline]
+    #[allow(dead_code)]
     pub(crate) fn dot_as_vec3(self, other: Self) -> Self {
-        #[cfg(vec3sse2)]
-        unsafe {
-            let dot_in_x = self.dot_as_m128(other);
-            Vec3(_mm_shuffle_ps(dot_in_x, dot_in_x, 0b00_00_00_00))
-        }
-
-        #[cfg(vec3f32)]
-        {
-            let dot = self.dot(other);
-            Vec3::new(dot, dot, dot)
-        }
+        let dot = self.dot(other);
+        Vec3::new(dot, dot, dot)
     }
 
     /// Computes the cross product of `self` and `other`.
     #[inline]
     pub fn cross(self, other: Self) -> Self {
-        #[cfg(vec3sse2)]
-        unsafe {
-            // x  <-  a.y*b.z - a.z*b.y
-            // y  <-  a.z*b.x - a.x*b.z
-            // z  <-  a.x*b.y - a.y*b.x
-            // We can save a shuffle by grouping it in this wacky order:
-            // (self.zxy() * other - self * other.zxy()).zxy()
-            let lhszxy = _mm_shuffle_ps(self.0, self.0, 0b01_01_00_10);
-            let rhszxy = _mm_shuffle_ps(other.0, other.0, 0b01_01_00_10);
-            let lhszxy_rhs = _mm_mul_ps(lhszxy, other.0);
-            let rhszxy_lhs = _mm_mul_ps(rhszxy, self.0);
-            let sub = _mm_sub_ps(lhszxy_rhs, rhszxy_lhs);
-            Self(_mm_shuffle_ps(sub, sub, 0b01_01_00_10))
-        }
-
-        #[cfg(vec3f32)]
-        {
-            Self(
-                self.1 * other.2 - other.1 * self.2,
-                self.2 * other.0 - other.2 * self.0,
-                self.0 * other.1 - other.0 * self.1,
-            )
-        }
+        Self(
+            self.1 * other.2 - other.1 * self.2,
+            self.2 * other.0 - other.2 * self.0,
+            self.0 * other.1 - other.0 * self.1,
+        )
     }
 
     /// Computes the length of `self`.
     #[inline]
     pub fn length(self) -> f32 {
-        #[cfg(vec3sse2)]
-        unsafe {
-            _mm_cvtss_f32(_mm_sqrt_ss(self.dot_as_m128(self)))
-        }
-
-        #[cfg(vec3f32)]
-        {
-            self.dot(self).sqrt()
-        }
+        self.dot(self).sqrt()
     }
 
     /// Computes the squared length of `self`.
@@ -480,19 +186,7 @@ impl Vec3 {
     /// For valid results, `self` must _not_ be of length zero.
     #[inline]
     pub fn length_reciprocal(self) -> f32 {
-        #[cfg(vec3sse2)]
-        {
-            let dot = self.dot_as_vec3(self);
-            unsafe {
-                // _mm_rsqrt_ps is lower precision
-                _mm_cvtss_f32(_mm_div_ps(_mm_set_ps1(1.0), _mm_sqrt_ps(dot.0)))
-            }
-        }
-
-        #[cfg(vec3f32)]
-        {
-            1.0 / self.length()
-        }
+        1.0 / self.length()
     }
 
     /// Returns `self` normalized to length 1.0.
@@ -500,16 +194,7 @@ impl Vec3 {
     /// For valid results, `self` must _not_ be of length zero.
     #[inline]
     pub fn normalize(self) -> Self {
-        #[cfg(vec3sse2)]
-        {
-            let dot = self.dot_as_vec3(self);
-            unsafe { Self(_mm_div_ps(self.0, _mm_sqrt_ps(dot.0))) }
-        }
-
-        #[cfg(vec3f32)]
-        {
-            self * self.length_reciprocal()
-        }
+        self * self.length_reciprocal()
     }
 
     /// Returns the vertical minimum of `self` and `other`.
@@ -519,19 +204,11 @@ impl Vec3 {
     /// taking the minimum of each element individually.
     #[inline]
     pub fn min(self, other: Self) -> Self {
-        #[cfg(vec3sse2)]
-        unsafe {
-            Self(_mm_min_ps(self.0, other.0))
-        }
-
-        #[cfg(vec3f32)]
-        {
-            Self(
-                self.0.min(other.0),
-                self.1.min(other.1),
-                self.2.min(other.2),
-            )
-        }
+        Self(
+            self.0.min(other.0),
+            self.1.min(other.1),
+            self.2.min(other.2),
+        )
     }
 
     /// Returns the vertical maximum of `self` and `other`.
@@ -541,19 +218,11 @@ impl Vec3 {
     /// taking the maximum of each element individually.
     #[inline]
     pub fn max(self, other: Self) -> Self {
-        #[cfg(vec3sse2)]
-        unsafe {
-            Self(_mm_max_ps(self.0, other.0))
-        }
-
-        #[cfg(vec3f32)]
-        {
-            Self(
-                self.0.max(other.0),
-                self.1.max(other.1),
-                self.2.max(other.2),
-            )
-        }
+        Self(
+            self.0.max(other.0),
+            self.1.max(other.1),
+            self.2.max(other.2),
+        )
     }
 
     /// Returns the horizontal minimum of `self`'s elements.
@@ -561,18 +230,7 @@ impl Vec3 {
     /// In other words, this computes `min(x, y, z)`.
     #[inline]
     pub fn min_element(self) -> f32 {
-        #[cfg(vec3sse2)]
-        unsafe {
-            let v = self.0;
-            let v = _mm_min_ps(v, _mm_shuffle_ps(v, v, 0b01_01_10_10));
-            let v = _mm_min_ps(v, _mm_shuffle_ps(v, v, 0b00_00_00_01));
-            _mm_cvtss_f32(v)
-        }
-
-        #[cfg(vec3f32)]
-        {
-            self.0.min(self.1.min(self.2))
-        }
+        self.0.min(self.1.min(self.2))
     }
 
     /// Returns the horizontal maximum of `self`'s elements.
@@ -580,18 +238,7 @@ impl Vec3 {
     /// In other words, this computes `max(x, y, z)`.
     #[inline]
     pub fn max_element(self) -> f32 {
-        #[cfg(vec3sse2)]
-        unsafe {
-            let v = self.0;
-            let v = _mm_max_ps(v, _mm_shuffle_ps(v, v, 0b00_00_10_10));
-            let v = _mm_max_ps(v, _mm_shuffle_ps(v, v, 0b00_00_00_01));
-            _mm_cvtss_f32(v)
-        }
-
-        #[cfg(vec3f32)]
-        {
-            self.0.max(self.1.max(self.2))
-        }
+        self.0.max(self.1.max(self.2))
     }
 
     /// Performs a vertical `==` comparison between `self` and `other`,
@@ -600,19 +247,11 @@ impl Vec3 {
     /// In other words, this computes `[x1 == x2, y1 == y2, z1 == z2, w1 == w2]`.
     #[inline]
     pub fn cmpeq(self, other: Self) -> Vec3Mask {
-        #[cfg(vec3sse2)]
-        unsafe {
-            Vec3Mask(_mm_cmpeq_ps(self.0, other.0))
-        }
-
-        #[cfg(vec3f32)]
-        {
-            Vec3Mask::new(
-                self.0.eq(&other.0),
-                self.1.eq(&other.1),
-                self.2.eq(&other.2),
-            )
-        }
+        Vec3Mask::new(
+            self.0.eq(&other.0),
+            self.1.eq(&other.1),
+            self.2.eq(&other.2),
+        )
     }
 
     /// Performs a vertical `!=` comparison between `self` and `other`,
@@ -621,19 +260,11 @@ impl Vec3 {
     /// In other words, this computes `[x1 != x2, y1 != y2, z1 != z2, w1 != w2]`.
     #[inline]
     pub fn cmpne(self, other: Self) -> Vec3Mask {
-        #[cfg(vec3sse2)]
-        unsafe {
-            Vec3Mask(_mm_cmpneq_ps(self.0, other.0))
-        }
-
-        #[cfg(vec3f32)]
-        {
-            Vec3Mask::new(
-                self.0.ne(&other.0),
-                self.1.ne(&other.1),
-                self.2.ne(&other.2),
-            )
-        }
+        Vec3Mask::new(
+            self.0.ne(&other.0),
+            self.1.ne(&other.1),
+            self.2.ne(&other.2),
+        )
     }
 
     /// Performs a vertical `>=` comparison between `self` and `other`,
@@ -642,19 +273,11 @@ impl Vec3 {
     /// In other words, this computes `[x1 >= x2, y1 >= y2, z1 >= z2, w1 >= w2]`.
     #[inline]
     pub fn cmpge(self, other: Self) -> Vec3Mask {
-        #[cfg(vec3sse2)]
-        unsafe {
-            Vec3Mask(_mm_cmpge_ps(self.0, other.0))
-        }
-
-        #[cfg(vec3f32)]
-        {
-            Vec3Mask::new(
-                self.0.ge(&other.0),
-                self.1.ge(&other.1),
-                self.2.ge(&other.2),
-            )
-        }
+        Vec3Mask::new(
+            self.0.ge(&other.0),
+            self.1.ge(&other.1),
+            self.2.ge(&other.2),
+        )
     }
 
     /// Performs a vertical `>` comparison between `self` and `other`,
@@ -663,19 +286,11 @@ impl Vec3 {
     /// In other words, this computes `[x1 > x2, y1 > y2, z1 > z2, w1 > w2]`.
     #[inline]
     pub fn cmpgt(self, other: Self) -> Vec3Mask {
-        #[cfg(vec3sse2)]
-        unsafe {
-            Vec3Mask(_mm_cmpgt_ps(self.0, other.0))
-        }
-
-        #[cfg(vec3f32)]
-        {
-            Vec3Mask::new(
-                self.0.gt(&other.0),
-                self.1.gt(&other.1),
-                self.2.gt(&other.2),
-            )
-        }
+        Vec3Mask::new(
+            self.0.gt(&other.0),
+            self.1.gt(&other.1),
+            self.2.gt(&other.2),
+        )
     }
 
     /// Performs a vertical `<=` comparison between `self` and `other`,
@@ -684,19 +299,11 @@ impl Vec3 {
     /// In other words, this computes `[x1 <= x2, y1 <= y2, z1 <= z2, w1 <= w2]`.
     #[inline]
     pub fn cmple(self, other: Self) -> Vec3Mask {
-        #[cfg(vec3sse2)]
-        unsafe {
-            Vec3Mask(_mm_cmple_ps(self.0, other.0))
-        }
-
-        #[cfg(vec3f32)]
-        {
-            Vec3Mask::new(
-                self.0.le(&other.0),
-                self.1.le(&other.1),
-                self.2.le(&other.2),
-            )
-        }
+        Vec3Mask::new(
+            self.0.le(&other.0),
+            self.1.le(&other.1),
+            self.2.le(&other.2),
+        )
     }
 
     /// Performs a vertical `<` comparison between `self` and `other`,
@@ -705,19 +312,11 @@ impl Vec3 {
     /// In other words, this computes `[x1 < x2, y1 < y2, z1 < z2, w1 < w2]`.
     #[inline]
     pub fn cmplt(self, other: Self) -> Vec3Mask {
-        #[cfg(vec3sse2)]
-        unsafe {
-            Vec3Mask(_mm_cmplt_ps(self.0, other.0))
-        }
-
-        #[cfg(vec3f32)]
-        {
-            Vec3Mask::new(
-                self.0.lt(&other.0),
-                self.1.lt(&other.1),
-                self.2.lt(&other.2),
-            )
-        }
+        Vec3Mask::new(
+            self.0.lt(&other.0),
+            self.1.lt(&other.1),
+            self.2.lt(&other.2),
+        )
     }
 
     /// Creates a new `Vec3` from the first four values in `slice`.
@@ -745,80 +344,35 @@ impl Vec3 {
 
     /// Per element multiplication/addition of the three inputs: b + (self * a)
     #[inline]
+    #[allow(dead_code)]
     pub(crate) fn mul_add(self, a: Self, b: Self) -> Self {
-        #[cfg(vec3sse2)]
-        unsafe {
-            Self(_mm_add_ps(_mm_mul_ps(self.0, a.0), b.0))
-        }
-
-        #[cfg(vec3f32)]
-        {
-            Self(
-                (self.0 * a.0) + b.0,
-                (self.1 * a.1) + b.1,
-                (self.2 * a.2) + b.2,
-            )
-        }
+        Self(
+            (self.0 * a.0) + b.0,
+            (self.1 * a.1) + b.1,
+            (self.2 * a.2) + b.2,
+        )
     }
 
     /// Returns a new `Vec3` containing the absolute value of each element of the original
     /// `Vec3`.
     #[inline]
     pub fn abs(self) -> Self {
-        #[cfg(vec3sse2)]
-        unsafe {
-            Self(_mm_and_ps(
-                self.0,
-                _mm_castsi128_ps(_mm_set1_epi32(0x7f_ff_ff_ff)),
-            ))
-        }
-
-        #[cfg(vec3f32)]
-        {
-            Self(self.0.abs(), self.1.abs(), self.2.abs())
-        }
+        Self(self.0.abs(), self.1.abs(), self.2.abs())
     }
 
     #[inline]
     pub fn round(self) -> Self {
-        #[cfg(vec3sse2)]
-        unsafe {
-            use crate::f32::funcs::sse2::m128_round;
-            Self(m128_round(self.0))
-        }
-
-        #[cfg(vec3f32)]
-        {
-            Self(self.0.round(), self.1.round(), self.2.round())
-        }
+        Self(self.0.round(), self.1.round(), self.2.round())
     }
 
     #[inline]
     pub fn floor(self) -> Self {
-        #[cfg(vec3sse2)]
-        unsafe {
-            use crate::f32::funcs::sse2::m128_floor;
-            Self(m128_floor(self.0))
-        }
-
-        #[cfg(vec3f32)]
-        {
-            Self(self.0.floor(), self.1.floor(), self.2.floor())
-        }
+        Self(self.0.floor(), self.1.floor(), self.2.floor())
     }
 
     #[inline]
     pub fn ceil(self) -> Self {
-        #[cfg(vec3sse2)]
-        unsafe {
-            use crate::f32::funcs::sse2::m128_ceil;
-            Self(m128_ceil(self.0))
-        }
-
-        #[cfg(vec3f32)]
-        {
-            Self(self.0.ceil(), self.1.ceil(), self.2.ceil())
-        }
+        Self(self.0.ceil(), self.1.ceil(), self.2.ceil())
     }
 
     /// Returns a new `Vec4` with elements representing the sign of `self`.
@@ -835,8 +389,7 @@ impl Vec3 {
     /// results in a new `Vec3`.
     #[inline]
     pub fn reciprocal(self) -> Self {
-        // TODO: Optimize
-        Self::one() / self
+        Self::new(1.0 / self.0, 1.0 / self.1, 1.0 / self.2)
     }
 
     /// Performs a linear interpolation between `self` and `other` based on
@@ -897,16 +450,7 @@ impl AsMut<[f32; 3]> for Vec3 {
 
 impl fmt::Display for Vec3 {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        #[cfg(vec3sse2)]
-        {
-            let (x, y, z) = (*self).into();
-            write!(f, "[{}, {}, {}]", x, y, z)
-        }
-
-        #[cfg(vec3f32)]
-        {
-            write!(f, "[{}, {}, {}]", self.0, self.1, self.2)
-        }
+        write!(f, "[{}, {}, {}]", self.0, self.1, self.2)
     }
 }
 
@@ -914,32 +458,16 @@ impl Div<Vec3> for Vec3 {
     type Output = Self;
     #[inline]
     fn div(self, other: Self) -> Self {
-        #[cfg(vec3sse2)]
-        unsafe {
-            Self(_mm_div_ps(self.0, other.0))
-        }
-
-        #[cfg(vec3f32)]
-        {
-            Self(self.0 / other.0, self.1 / other.1, self.2 / other.2)
-        }
+        Self(self.0 / other.0, self.1 / other.1, self.2 / other.2)
     }
 }
 
 impl DivAssign<Vec3> for Vec3 {
     #[inline]
     fn div_assign(&mut self, other: Self) {
-        #[cfg(vec3sse2)]
-        {
-            self.0 = unsafe { _mm_div_ps(self.0, other.0) };
-        }
-
-        #[cfg(vec3f32)]
-        {
-            self.0 /= other.0;
-            self.1 /= other.1;
-            self.2 /= other.2;
-        }
+        self.0 /= other.0;
+        self.1 /= other.1;
+        self.2 /= other.2;
     }
 }
 
@@ -947,32 +475,24 @@ impl Div<f32> for Vec3 {
     type Output = Self;
     #[inline]
     fn div(self, other: f32) -> Self {
-        #[cfg(vec3sse2)]
-        unsafe {
-            Self(_mm_div_ps(self.0, _mm_set1_ps(other)))
-        }
-
-        #[cfg(vec3f32)]
-        {
-            Self(self.0 / other, self.1 / other, self.2 / other)
-        }
+        Self(self.0 / other, self.1 / other, self.2 / other)
     }
 }
 
 impl DivAssign<f32> for Vec3 {
     #[inline]
     fn div_assign(&mut self, other: f32) {
-        #[cfg(vec3sse2)]
-        {
-            self.0 = unsafe { _mm_div_ps(self.0, _mm_set1_ps(other)) };
-        }
+        self.0 /= other;
+        self.1 /= other;
+        self.2 /= other;
+    }
+}
 
-        #[cfg(vec3f32)]
-        {
-            self.0 /= other;
-            self.1 /= other;
-            self.2 /= other;
-        }
+impl Div<Vec3> for f32 {
+    type Output = Vec3;
+    #[inline]
+    fn div(self, other: Vec3) -> Vec3 {
+        Vec3(self / other.0, self / other.1, self / other.2)
     }
 }
 
@@ -980,32 +500,16 @@ impl Mul<Vec3> for Vec3 {
     type Output = Self;
     #[inline]
     fn mul(self, other: Self) -> Self {
-        #[cfg(vec3sse2)]
-        unsafe {
-            Self(_mm_mul_ps(self.0, other.0))
-        }
-
-        #[cfg(vec3f32)]
-        {
-            Self(self.0 * other.0, self.1 * other.1, self.2 * other.2)
-        }
+        Self(self.0 * other.0, self.1 * other.1, self.2 * other.2)
     }
 }
 
 impl MulAssign<Vec3> for Vec3 {
     #[inline]
     fn mul_assign(&mut self, other: Self) {
-        #[cfg(vec3sse2)]
-        {
-            self.0 = unsafe { _mm_mul_ps(self.0, other.0) };
-        }
-
-        #[cfg(vec3f32)]
-        {
-            self.0 *= other.0;
-            self.1 *= other.1;
-            self.2 *= other.2;
-        }
+        self.0 *= other.0;
+        self.1 *= other.1;
+        self.2 *= other.2;
     }
 }
 
@@ -1013,32 +517,16 @@ impl Mul<f32> for Vec3 {
     type Output = Self;
     #[inline]
     fn mul(self, other: f32) -> Self {
-        #[cfg(vec3sse2)]
-        unsafe {
-            Self(_mm_mul_ps(self.0, _mm_set1_ps(other)))
-        }
-
-        #[cfg(vec3f32)]
-        {
-            Self(self.0 * other, self.1 * other, self.2 * other)
-        }
+        Self(self.0 * other, self.1 * other, self.2 * other)
     }
 }
 
 impl MulAssign<f32> for Vec3 {
     #[inline]
     fn mul_assign(&mut self, other: f32) {
-        #[cfg(vec3sse2)]
-        {
-            self.0 = unsafe { _mm_mul_ps(self.0, _mm_set1_ps(other)) };
-        }
-
-        #[cfg(vec3f32)]
-        {
-            self.0 *= other;
-            self.1 *= other;
-            self.2 *= other;
-        }
+        self.0 *= other;
+        self.1 *= other;
+        self.2 *= other;
     }
 }
 
@@ -1046,15 +534,7 @@ impl Mul<Vec3> for f32 {
     type Output = Vec3;
     #[inline]
     fn mul(self, other: Vec3) -> Vec3 {
-        #[cfg(vec3sse2)]
-        unsafe {
-            Vec3(_mm_mul_ps(_mm_set1_ps(self), other.0))
-        }
-
-        #[cfg(vec3f32)]
-        {
-            Vec3(self * other.0, self * other.1, self * other.2)
-        }
+        Vec3(self * other.0, self * other.1, self * other.2)
     }
 }
 
@@ -1062,32 +542,16 @@ impl Add for Vec3 {
     type Output = Self;
     #[inline]
     fn add(self, other: Self) -> Self {
-        #[cfg(vec3sse2)]
-        unsafe {
-            Self(_mm_add_ps(self.0, other.0))
-        }
-
-        #[cfg(vec3f32)]
-        {
-            Self(self.0 + other.0, self.1 + other.1, self.2 + other.2)
-        }
+        Self(self.0 + other.0, self.1 + other.1, self.2 + other.2)
     }
 }
 
 impl AddAssign for Vec3 {
     #[inline]
     fn add_assign(&mut self, other: Self) {
-        #[cfg(vec3sse2)]
-        {
-            self.0 = unsafe { _mm_add_ps(self.0, other.0) };
-        }
-
-        #[cfg(vec3f32)]
-        {
-            self.0 += other.0;
-            self.1 += other.1;
-            self.2 += other.2;
-        }
+        self.0 += other.0;
+        self.1 += other.1;
+        self.2 += other.2;
     }
 }
 
@@ -1095,32 +559,16 @@ impl Sub for Vec3 {
     type Output = Self;
     #[inline]
     fn sub(self, other: Self) -> Self {
-        #[cfg(vec3sse2)]
-        unsafe {
-            Self(_mm_sub_ps(self.0, other.0))
-        }
-
-        #[cfg(vec3f32)]
-        {
-            Self(self.0 - other.0, self.1 - other.1, self.2 - other.2)
-        }
+        Self(self.0 - other.0, self.1 - other.1, self.2 - other.2)
     }
 }
 
 impl SubAssign for Vec3 {
     #[inline]
     fn sub_assign(&mut self, other: Self) {
-        #[cfg(vec3sse2)]
-        {
-            self.0 = unsafe { _mm_sub_ps(self.0, other.0) };
-        }
-
-        #[cfg(vec3f32)]
-        {
-            self.0 -= other.0;
-            self.1 -= other.1;
-            self.2 -= other.2;
-        }
+        self.0 -= other.0;
+        self.1 -= other.1;
+        self.2 -= other.2;
     }
 }
 
@@ -1128,15 +576,7 @@ impl Neg for Vec3 {
     type Output = Self;
     #[inline]
     fn neg(self) -> Self {
-        #[cfg(vec3sse2)]
-        unsafe {
-            Self(_mm_sub_ps(_mm_set1_ps(0.0), self.0))
-        }
-
-        #[cfg(vec3f32)]
-        {
-            Self(-self.0, -self.1, -self.2)
-        }
+        Self(-self.0, -self.1, -self.2)
     }
 }
 
@@ -1165,20 +605,7 @@ impl From<(f32, f32, f32)> for Vec3 {
 impl From<Vec3> for (f32, f32, f32) {
     #[inline]
     fn from(v: Vec3) -> Self {
-        #[cfg(vec3sse2)]
-        {
-            let mut out: MaybeUninit<Align16<(f32, f32, f32)>> = MaybeUninit::uninit();
-            unsafe {
-                // out is 16 bytes in size due to alignment
-                _mm_store_ps(out.as_mut_ptr() as *mut f32, v.0);
-                out.assume_init().0
-            }
-        }
-
-        #[cfg(vec3f32)]
-        {
-            (v.0, v.1, v.2)
-        }
+        (v.0, v.1, v.2)
     }
 }
 
@@ -1192,19 +619,22 @@ impl From<[f32; 3]> for Vec3 {
 impl From<Vec3> for [f32; 3] {
     #[inline]
     fn from(v: Vec3) -> Self {
-        #[cfg(vec3sse2)]
+        [v.0, v.1, v.2]
+    }
+}
+
+impl From<Vec3A> for Vec3 {
+    #[inline]
+    fn from(v: Vec3A) -> Self {
+        #[cfg(vec3a_sse2)]
         {
-            let mut out: MaybeUninit<Align16<[f32; 3]>> = MaybeUninit::uninit();
-            unsafe {
-                // out is 16 bytes in size due to alignment
-                _mm_store_ps(out.as_mut_ptr() as *mut f32, v.0);
-                out.assume_init().0
-            }
+            let (x, y, z) = v.into();
+            Self(x, y, z)
         }
 
-        #[cfg(vec3f32)]
+        #[cfg(vec3a_f32)]
         {
-            [v.0, v.1, v.2]
+            v.0
         }
     }
 }
diff --git a/src/f32/vec3_mask.rs b/src/f32/vec3_mask.rs
index ec5db44..4f6bcde 100644
--- a/src/f32/vec3_mask.rs
+++ b/src/f32/vec3_mask.rs
@@ -1,73 +1,11 @@
 use super::Vec3;
 use core::{fmt, ops::*};
 
-#[cfg(all(vec3sse2, target_arch = "x86"))]
-use core::arch::x86::*;
-#[cfg(all(vec3sse2, target_arch = "x86_64"))]
-use core::arch::x86_64::*;
-#[cfg(vec3sse2)]
-use core::{cmp::Ordering, hash};
-
 /// A 3-dimensional vector mask.
-///
-/// This type is typically created by comparison methods on `Vec3`.  It is
-/// essentially a vector of three boolean values.
-#[cfg(vec3sse2)]
-#[derive(Clone, Copy)]
-#[repr(C)]
-pub struct Vec3Mask(pub(crate) __m128);
-
-/// A 3-dimensional vector mask.
-#[cfg(vec3f32)]
 #[derive(Clone, Copy, Default, PartialEq, Eq, Ord, PartialOrd, Hash)]
-// if compiling with simd enabled assume alignment needs to match the simd type
-#[cfg_attr(vec3f32_align16, repr(align(16)))]
 #[repr(C)]
 pub struct Vec3Mask(pub(crate) u32, pub(crate) u32, pub(crate) u32);
 
-#[cfg(vec3sse2)]
-impl Default for Vec3Mask {
-    #[inline]
-    fn default() -> Self {
-        unsafe { Self(_mm_setzero_ps()) }
-    }
-}
-
-#[cfg(vec3sse2)]
-impl PartialEq for Vec3Mask {
-    #[inline]
-    fn eq(&self, other: &Self) -> bool {
-        self.as_ref().eq(other.as_ref())
-    }
-}
-
-#[cfg(vec3sse2)]
-impl Eq for Vec3Mask {}
-
-#[cfg(vec3sse2)]
-impl Ord for Vec3Mask {
-    #[inline]
-    fn cmp(&self, other: &Self) -> Ordering {
-        self.as_ref().cmp(other.as_ref())
-    }
-}
-
-#[cfg(vec3sse2)]
-impl PartialOrd for Vec3Mask {
-    #[inline]
-    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
-        Some(self.cmp(other))
-    }
-}
-
-#[cfg(vec3sse2)]
-impl hash::Hash for Vec3Mask {
-    #[inline]
-    fn hash<H: hash::Hasher>(&self, state: &mut H) {
-        self.as_ref().hash(state);
-    }
-}
-
 impl Vec3Mask {
     /// Creates a new `Vec3Mask`.
     #[inline]
@@ -75,22 +13,8 @@ impl Vec3Mask {
         // A SSE2 mask can be any bit pattern but for the `Vec3Mask` implementation of select we
         // expect either 0 or 0xff_ff_ff_ff. This should be a safe assumption as this type can only
         // be created via this function or by `Vec3` methods.
-
         const MASK: [u32; 2] = [0, 0xff_ff_ff_ff];
-        #[cfg(vec3sse2)]
-        unsafe {
-            Self(_mm_set_ps(
-                f32::from_bits(MASK[z as usize]),
-                f32::from_bits(MASK[z as usize]),
-                f32::from_bits(MASK[y as usize]),
-                f32::from_bits(MASK[x as usize]),
-            ))
-        }
-
-        #[cfg(vec3f32)]
-        {
-            Self(MASK[x as usize], MASK[y as usize], MASK[z as usize])
-        }
+        Self(MASK[x as usize], MASK[y as usize], MASK[z as usize])
     }
 
     /// Returns a bitmask with the lowest three bits set from the elements of
@@ -103,16 +27,7 @@ impl Vec3Mask {
     pub fn bitmask(&self) -> u32 {
         // _mm_movemask_ps only checks the most significant bit of the u32 is
         // true, so we replicate that here with the non-SSE2 version.
-
-        #[cfg(vec3sse2)]
-        unsafe {
-            (_mm_movemask_ps(self.0) as u32) & 0x7
-        }
-
-        #[cfg(vec3f32)]
-        {
-            (self.0 & 0x1) | (self.1 & 0x1) << 1 | (self.2 & 0x1) << 2
-        }
+        (self.0 & 0x1) | (self.1 & 0x1) << 1 | (self.2 & 0x1) << 2
     }
 
     /// Returns true if any of the elements are true, false otherwise.
@@ -120,15 +35,7 @@ impl Vec3Mask {
     /// In other words: `x || y || z`.
     #[inline]
     pub fn any(&self) -> bool {
-        #[cfg(vec3sse2)]
-        unsafe {
-            (_mm_movemask_ps(self.0) & 0x7) != 0
-        }
-
-        #[cfg(vec3f32)]
-        {
-            ((self.0 | self.1 | self.2) & 0x1) != 0
-        }
+        ((self.0 | self.1 | self.2) & 0x1) != 0
     }
 
     /// Returns true if all the elements are true, false otherwise.
@@ -136,15 +43,7 @@ impl Vec3Mask {
     /// In other words: `x && y && z`.
     #[inline]
     pub fn all(&self) -> bool {
-        #[cfg(vec3sse2)]
-        unsafe {
-            (_mm_movemask_ps(self.0) & 0x7) == 0x7
-        }
-
-        #[cfg(vec3f32)]
-        {
-            ((self.0 & self.1 & self.2) & 0x1) != 0
-        }
+        ((self.0 & self.1 & self.2) & 0x1) != 0
     }
 
     /// Creates a new `Vec3` from the elements in `if_true` and `if_false`,
@@ -156,23 +55,11 @@ impl Vec3Mask {
     pub fn select(self, if_true: Vec3, if_false: Vec3) -> Vec3 {
         // We are assuming that the mask values are either 0 or 0xff_ff_ff_ff for the SSE2 and f32
         // to behave the same here.
-
-        #[cfg(vec3sse2)]
-        unsafe {
-            Vec3(_mm_or_ps(
-                _mm_andnot_ps(self.0, if_false.0),
-                _mm_and_ps(if_true.0, self.0),
-            ))
-        }
-
-        #[cfg(vec3f32)]
-        {
-            Vec3(
-                if self.0 != 0 { if_true.0 } else { if_false.0 },
-                if self.1 != 0 { if_true.1 } else { if_false.1 },
-                if self.2 != 0 { if_true.2 } else { if_false.2 },
-            )
-        }
+        Vec3(
+            if self.0 != 0 { if_true.0 } else { if_false.0 },
+            if self.1 != 0 { if_true.1 } else { if_false.1 },
+            if self.2 != 0 { if_true.2 } else { if_false.2 },
+        )
     }
 }
 
@@ -180,32 +67,16 @@ impl BitAnd for Vec3Mask {
     type Output = Self;
     #[inline]
     fn bitand(self, other: Self) -> Self {
-        #[cfg(vec3sse2)]
-        unsafe {
-            Self(_mm_and_ps(self.0, other.0))
-        }
-
-        #[cfg(vec3f32)]
-        {
-            Self(self.0 & other.0, self.1 & other.1, self.2 & other.2)
-        }
+        Self(self.0 & other.0, self.1 & other.1, self.2 & other.2)
     }
 }
 
 impl BitAndAssign for Vec3Mask {
     #[inline]
     fn bitand_assign(&mut self, other: Self) {
-        #[cfg(vec3sse2)]
-        {
-            self.0 = unsafe { _mm_and_ps(self.0, other.0) };
-        }
-
-        #[cfg(vec3f32)]
-        {
-            self.0 &= other.0;
-            self.1 &= other.1;
-            self.2 &= other.2;
-        }
+        self.0 &= other.0;
+        self.1 &= other.1;
+        self.2 &= other.2;
     }
 }
 
@@ -213,32 +84,16 @@ impl BitOr for Vec3Mask {
     type Output = Self;
     #[inline]
     fn bitor(self, other: Self) -> Self {
-        #[cfg(vec3sse2)]
-        unsafe {
-            Self(_mm_or_ps(self.0, other.0))
-        }
-
-        #[cfg(vec3f32)]
-        {
-            Self(self.0 | other.0, self.1 | other.1, self.2 | other.2)
-        }
+        Self(self.0 | other.0, self.1 | other.1, self.2 | other.2)
     }
 }
 
 impl BitOrAssign for Vec3Mask {
     #[inline]
     fn bitor_assign(&mut self, other: Self) {
-        #[cfg(vec3sse2)]
-        {
-            self.0 = unsafe { _mm_or_ps(self.0, other.0) };
-        }
-
-        #[cfg(vec3f32)]
-        {
-            self.0 |= other.0;
-            self.1 |= other.1;
-            self.2 |= other.2;
-        }
+        self.0 |= other.0;
+        self.1 |= other.1;
+        self.2 |= other.2;
     }
 }
 
@@ -246,33 +101,13 @@ impl Not for Vec3Mask {
     type Output = Self;
     #[inline]
     fn not(self) -> Self {
-        #[cfg(vec3sse2)]
-        unsafe {
-            Self(_mm_andnot_ps(
-                self.0,
-                _mm_set_ps1(f32::from_bits(0xff_ff_ff_ff)),
-            ))
-        }
-
-        #[cfg(vec3f32)]
-        {
-            Self(!self.0, !self.1, !self.2)
-        }
+        Self(!self.0, !self.1, !self.2)
     }
 }
 
 impl fmt::Debug for Vec3Mask {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        #[cfg(vec3sse2)]
-        {
-            let arr = self.as_ref();
-            write!(f, "Vec3Mask({:#x}, {:#x}, {:#x})", arr[0], arr[1], arr[2])
-        }
-
-        #[cfg(vec3f32)]
-        {
-            write!(f, "Vec3Mask({:#x}, {:#x}, {:#x})", self.0, self.1, self.2)
-        }
+        write!(f, "Vec3Mask({:#x}, {:#x}, {:#x})", self.0, self.1, self.2)
     }
 }
 
diff --git a/src/f32/vec3a.rs b/src/f32/vec3a.rs
new file mode 100644
index 0000000..9112490
--- /dev/null
+++ b/src/f32/vec3a.rs
@@ -0,0 +1,1196 @@
+use super::{Vec2, Vec3, Vec3AMask, Vec4};
+use core::{fmt, ops::*};
+
+#[cfg(all(vec3a_sse2, target_arch = "x86"))]
+use core::arch::x86::*;
+#[cfg(all(vec3a_sse2, target_arch = "x86_64"))]
+use core::arch::x86_64::*;
+
+#[cfg(vec3a_sse2)]
+use core::{cmp::Ordering, f32, mem::MaybeUninit};
+
+#[cfg(vec3a_sse2)]
+use crate::{
+    f32::{X_AXIS, Y_AXIS, Z_AXIS},
+    Align16,
+};
+
+/// A 3-dimensional vector.
+///
+/// This type uses 16 byte aligned SIMD vector4 types for storage on supported platforms for better
+/// performance than the `Vec3` type.
+///
+/// It is possible to convert between `Vec3` and `Vec3A` types using `From` trait implementations.
+#[cfg(vec3a_sse2)]
+#[derive(Clone, Copy, Debug)]
+#[repr(C)]
+pub struct Vec3A(pub(crate) __m128);
+
+/// A 3-dimensional vector.
+#[cfg(vec3a_f32)]
+#[derive(Clone, Copy, PartialEq, PartialOrd, Debug, Default)]
+#[repr(align(16), C)]
+pub struct Vec3A(pub(crate) Vec3);
+
+#[cfg(vec3a_sse2)]
+impl Vec3A {
+    /// Calculates the Vec3A dot product and returns answer in x lane of __m128.
+    #[inline]
+    unsafe fn dot_as_m128(self, other: Self) -> __m128 {
+        let x2_y2_z2_w2 = _mm_mul_ps(self.0, other.0);
+        let y2_0_0_0 = _mm_shuffle_ps(x2_y2_z2_w2, x2_y2_z2_w2, 0b00_00_00_01);
+        let z2_0_0_0 = _mm_shuffle_ps(x2_y2_z2_w2, x2_y2_z2_w2, 0b00_00_00_10);
+        let x2y2_0_0_0 = _mm_add_ss(x2_y2_z2_w2, y2_0_0_0);
+        _mm_add_ss(x2y2_0_0_0, z2_0_0_0)
+    }
+}
+
+#[cfg(vec3a_sse2)]
+impl Default for Vec3A {
+    #[inline]
+    fn default() -> Self {
+        Vec3A::zero()
+    }
+}
+
+#[cfg(vec3a_sse2)]
+impl PartialEq for Vec3A {
+    #[inline]
+    fn eq(&self, other: &Self) -> bool {
+        self.cmpeq(*other).all()
+    }
+}
+
+#[cfg(vec3a_sse2)]
+impl PartialOrd for Vec3A {
+    #[inline]
+    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+        self.as_ref().partial_cmp(other.as_ref())
+    }
+}
+
+#[cfg(vec3a_sse2)]
+impl From<Vec3A> for __m128 {
+    // TODO: write test
+    #[inline]
+    fn from(t: Vec3A) -> Self {
+        t.0
+    }
+}
+
+#[cfg(vec3a_sse2)]
+impl From<__m128> for Vec3A {
+    #[inline]
+    fn from(t: __m128) -> Self {
+        Self(t)
+    }
+}
+
+#[inline]
+pub fn vec3a(x: f32, y: f32, z: f32) -> Vec3A {
+    Vec3A::new(x, y, z)
+}
+
+impl Vec3A {
+    /// Creates a new `Vec3A`.
+    #[inline]
+    pub fn new(x: f32, y: f32, z: f32) -> Self {
+        #[cfg(vec3a_sse2)]
+        unsafe {
+            Self(_mm_set_ps(z, z, y, x))
+        }
+
+        #[cfg(vec3a_f32)]
+        {
+            Self(Vec3::new(x, y, z))
+        }
+    }
+
+    /// Creates a new `Vec3A` with all elements set to `0.0`.
+    #[inline]
+    pub fn zero() -> Self {
+        #[cfg(vec3a_sse2)]
+        unsafe {
+            Self(_mm_setzero_ps())
+        }
+
+        #[cfg(vec3a_f32)]
+        {
+            Self(Vec3::zero())
+        }
+    }
+
+    /// Creates a new `Vec3A` with all elements set to `1.0`.
+    #[inline]
+    pub fn one() -> Self {
+        #[cfg(vec3a_sse2)]
+        unsafe {
+            Self(_mm_set1_ps(1.0))
+        }
+
+        #[cfg(vec3a_f32)]
+        {
+            Self(Vec3::one())
+        }
+    }
+
+    /// Creates a new `Vec3A` with values `[x: 1.0, y: 0.0, z: 0.0]`.
+    #[inline]
+    pub fn unit_x() -> Self {
+        #[cfg(vec3a_sse2)]
+        unsafe {
+            Self(_mm_load_ps(X_AXIS.0.as_ptr()))
+        }
+
+        #[cfg(vec3a_f32)]
+        {
+            Self(Vec3::unit_x())
+        }
+    }
+
+    /// Creates a new `Vec3A` with values `[x: 0.0, y: 1.0, z: 0.0]`.
+    #[inline]
+    pub fn unit_y() -> Self {
+        #[cfg(vec3a_sse2)]
+        unsafe {
+            Self(_mm_load_ps(Y_AXIS.0.as_ptr()))
+        }
+
+        #[cfg(vec3a_f32)]
+        {
+            Self(Vec3::unit_y())
+        }
+    }
+
+    /// Creates a new `Vec3A` with values `[x: 0.0, y: 0.0, z: 1.0]`.
+    #[inline]
+    pub fn unit_z() -> Self {
+        #[cfg(vec3a_sse2)]
+        unsafe {
+            Self(_mm_load_ps(Z_AXIS.0.as_ptr()))
+        }
+
+        #[cfg(vec3a_f32)]
+        {
+            Self(Vec3::unit_z())
+        }
+    }
+
+    /// Creates a new `Vec3A` with all elements set to `v`.
+    #[inline]
+    pub fn splat(v: f32) -> Self {
+        #[cfg(vec3a_sse2)]
+        unsafe {
+            Self(_mm_set_ps1(v))
+        }
+
+        #[cfg(vec3a_f32)]
+        {
+            Self(Vec3::splat(v))
+        }
+    }
+
+    /// Creates a new `Vec4` from `self` and the given `w` value.
+    #[inline]
+    pub fn extend(self, w: f32) -> Vec4 {
+        #[cfg(vec3a_sse2)]
+        {
+            let mut temp: Vec4 = self.0.into();
+            temp.set_w(w);
+            temp
+        }
+
+        #[cfg(vec3a_f32)]
+        {
+            self.0.extend(w)
+        }
+    }
+
+    /// Creates a `Vec2` from the first three elements of `self`,
+    /// removing `z`.
+    #[inline]
+    pub fn truncate(self) -> Vec2 {
+        #[cfg(vec3a_sse2)]
+        {
+            let (x, y, _) = self.into();
+            Vec2::new(x, y)
+        }
+
+        #[cfg(vec3a_f32)]
+        {
+            self.0.truncate()
+        }
+    }
+
+    /// Returns element `x`.
+    #[inline]
+    pub fn x(self) -> f32 {
+        #[cfg(vec3a_sse2)]
+        unsafe {
+            _mm_cvtss_f32(self.0)
+        }
+
+        #[cfg(vec3a_f32)]
+        {
+            self.0.x()
+        }
+    }
+
+    /// Returns element `y`.
+    #[inline]
+    pub fn y(self) -> f32 {
+        #[cfg(vec3a_sse2)]
+        unsafe {
+            _mm_cvtss_f32(_mm_shuffle_ps(self.0, self.0, 0b01_01_01_01))
+        }
+
+        #[cfg(vec3a_f32)]
+        {
+            self.0.y()
+        }
+    }
+
+    /// Returns element `z`.
+    #[inline]
+    pub fn z(self) -> f32 {
+        #[cfg(vec3a_sse2)]
+        unsafe {
+            _mm_cvtss_f32(_mm_shuffle_ps(self.0, self.0, 0b10_10_10_10))
+        }
+
+        #[cfg(vec3a_f32)]
+        {
+            self.0.z()
+        }
+    }
+
+    /// Returns a mutable reference to element `x`.
+    #[inline]
+    pub fn x_mut(&mut self) -> &mut f32 {
+        #[cfg(vec3a_sse2)]
+        unsafe {
+            &mut *(self as *mut Self as *mut f32)
+        }
+
+        #[cfg(vec3a_f32)]
+        {
+            self.0.x_mut()
+        }
+    }
+
+    /// Returns a mutable reference to element `y`.
+    #[inline]
+    pub fn y_mut(&mut self) -> &mut f32 {
+        #[cfg(vec3a_sse2)]
+        unsafe {
+            &mut *(self as *mut Self as *mut f32).offset(1)
+        }
+
+        #[cfg(vec3a_f32)]
+        {
+            self.0.y_mut()
+        }
+    }
+
+    /// Returns a mutable reference to element `z`.
+    #[inline]
+    pub fn z_mut(&mut self) -> &mut f32 {
+        #[cfg(vec3a_sse2)]
+        unsafe {
+            &mut *(self as *mut Self as *mut f32).offset(2)
+        }
+
+        #[cfg(vec3a_f32)]
+        {
+            self.0.z_mut()
+        }
+    }
+
+    /// Sets element `x`.
+    #[inline]
+    pub fn set_x(&mut self, x: f32) {
+        #[cfg(vec3a_sse2)]
+        unsafe {
+            self.0 = _mm_move_ss(self.0, _mm_set_ss(x));
+        }
+
+        #[cfg(vec3a_f32)]
+        {
+            *self.0.x_mut() = x;
+        }
+    }
+
+    /// Sets element `y`.
+    #[inline]
+    pub fn set_y(&mut self, y: f32) {
+        #[cfg(vec3a_sse2)]
+        unsafe {
+            let mut t = _mm_move_ss(self.0, _mm_set_ss(y));
+            t = _mm_shuffle_ps(t, t, 0b11_10_00_00);
+            self.0 = _mm_move_ss(t, self.0);
+        }
+
+        #[cfg(vec3a_f32)]
+        {
+            *self.0.y_mut() = y;
+        }
+    }
+
+    /// Sets element `z`.
+    #[inline]
+    pub fn set_z(&mut self, z: f32) {
+        #[cfg(vec3a_sse2)]
+        unsafe {
+            let mut t = _mm_move_ss(self.0, _mm_set_ss(z));
+            t = _mm_shuffle_ps(t, t, 0b11_00_01_00);
+            self.0 = _mm_move_ss(t, self.0);
+        }
+
+        #[cfg(vec3a_f32)]
+        {
+            *self.0.z_mut() = z;
+        }
+    }
+
+    /// Returns a `Vec3A` with all elements set to the value of element `x`.
+    #[inline]
+    #[allow(dead_code)]
+    pub(crate) fn dup_x(self) -> Self {
+        #[cfg(vec3a_sse2)]
+        unsafe {
+            Self(_mm_shuffle_ps(self.0, self.0, 0b00_00_00_00))
+        }
+
+        #[cfg(vec3a_f32)]
+        {
+            Self(self.0.dup_x())
+        }
+    }
+
+    /// Returns a `Vec3A` with all elements set to the value of element `y`.
+    #[inline]
+    #[allow(dead_code)]
+    pub(crate) fn dup_y(self) -> Self {
+        #[cfg(vec3a_sse2)]
+        unsafe {
+            Self(_mm_shuffle_ps(self.0, self.0, 0b01_01_01_01))
+        }
+
+        #[cfg(vec3a_f32)]
+        {
+            Self(self.0.dup_y())
+        }
+    }
+
+    /// Returns a `Vec3A` with all elements set to the value of element `z`.
+    #[inline]
+    #[allow(dead_code)]
+    pub(crate) fn dup_z(self) -> Self {
+        #[cfg(vec3a_sse2)]
+        unsafe {
+            Self(_mm_shuffle_ps(self.0, self.0, 0b10_10_10_10))
+        }
+
+        #[cfg(vec3a_f32)]
+        {
+            Self(self.0.dup_z())
+        }
+    }
+
+    /// Computes the dot product of `self` and `other`.
+    #[inline]
+    pub fn dot(self, other: Self) -> f32 {
+        #[cfg(vec3a_sse2)]
+        unsafe {
+            _mm_cvtss_f32(self.dot_as_m128(other))
+        }
+
+        #[cfg(vec3a_f32)]
+        {
+            self.0.dot(other.0)
+        }
+    }
+
+    /// Returns Vec3A dot in all lanes of Vec3A
+    #[inline]
+    #[allow(dead_code)]
+    pub(crate) fn dot_as_vec3(self, other: Self) -> Self {
+        #[cfg(vec3a_sse2)]
+        unsafe {
+            let dot_in_x = self.dot_as_m128(other);
+            Vec3A(_mm_shuffle_ps(dot_in_x, dot_in_x, 0b00_00_00_00))
+        }
+
+        #[cfg(vec3a_f32)]
+        {
+            Self(self.0.dot_as_vec3(other.0))
+        }
+    }
+
+    /// Computes the cross product of `self` and `other`.
+    #[inline]
+    pub fn cross(self, other: Self) -> Self {
+        #[cfg(vec3a_sse2)]
+        unsafe {
+            // x  <-  a.y*b.z - a.z*b.y
+            // y  <-  a.z*b.x - a.x*b.z
+            // z  <-  a.x*b.y - a.y*b.x
+            // We can save a shuffle by grouping it in this wacky order:
+            // (self.zxy() * other - self * other.zxy()).zxy()
+            let lhszxy = _mm_shuffle_ps(self.0, self.0, 0b01_01_00_10);
+            let rhszxy = _mm_shuffle_ps(other.0, other.0, 0b01_01_00_10);
+            let lhszxy_rhs = _mm_mul_ps(lhszxy, other.0);
+            let rhszxy_lhs = _mm_mul_ps(rhszxy, self.0);
+            let sub = _mm_sub_ps(lhszxy_rhs, rhszxy_lhs);
+            Self(_mm_shuffle_ps(sub, sub, 0b01_01_00_10))
+        }
+
+        #[cfg(vec3a_f32)]
+        {
+            Self(self.0.cross(other.0))
+        }
+    }
+
+    /// Computes the length of `self`.
+    #[inline]
+    pub fn length(self) -> f32 {
+        #[cfg(vec3a_sse2)]
+        unsafe {
+            _mm_cvtss_f32(_mm_sqrt_ss(self.dot_as_m128(self)))
+        }
+
+        #[cfg(vec3a_f32)]
+        {
+            self.0.length()
+        }
+    }
+
+    /// Computes the squared length of `self`.
+    ///
+    /// This is generally faster than `Vec3A::length()` as it avoids a square
+    /// root operation.
+    #[inline]
+    pub fn length_squared(self) -> f32 {
+        self.dot(self)
+    }
+
+    /// Computes `1.0 / Vec3A::length()`.
+    ///
+    /// For valid results, `self` must _not_ be of length zero.
+    #[inline]
+    pub fn length_reciprocal(self) -> f32 {
+        #[cfg(vec3a_sse2)]
+        {
+            let dot = self.dot_as_vec3(self);
+            unsafe {
+                // _mm_rsqrt_ps is lower precision
+                _mm_cvtss_f32(_mm_div_ps(_mm_set_ps1(1.0), _mm_sqrt_ps(dot.0)))
+            }
+        }
+
+        #[cfg(vec3a_f32)]
+        {
+            self.0.length_reciprocal()
+        }
+    }
+
+    /// Returns `self` normalized to length 1.0.
+    ///
+    /// For valid results, `self` must _not_ be of length zero.
+    #[inline]
+    pub fn normalize(self) -> Self {
+        #[cfg(vec3a_sse2)]
+        {
+            let dot = self.dot_as_vec3(self);
+            unsafe { Self(_mm_div_ps(self.0, _mm_sqrt_ps(dot.0))) }
+        }
+
+        #[cfg(vec3a_f32)]
+        {
+            Self(self.0.normalize())
+        }
+    }
+
+    /// Returns the vertical minimum of `self` and `other`.
+    ///
+    /// In other words, this computes
+    /// `[x: min(x1, x2), y: min(y1, y2), z: min(z1, z2)]`,
+    /// taking the minimum of each element individually.
+    #[inline]
+    pub fn min(self, other: Self) -> Self {
+        #[cfg(vec3a_sse2)]
+        unsafe {
+            Self(_mm_min_ps(self.0, other.0))
+        }
+
+        #[cfg(vec3a_f32)]
+        {
+            Self(self.0.min(other.0))
+        }
+    }
+
+    /// Returns the vertical maximum of `self` and `other`.
+    ///
+    /// In other words, this computes
+    /// `[x: max(x1, x2), y: max(y1, y2), z: max(z1, z2)]`,
+    /// taking the maximum of each element individually.
+    #[inline]
+    pub fn max(self, other: Self) -> Self {
+        #[cfg(vec3a_sse2)]
+        unsafe {
+            Self(_mm_max_ps(self.0, other.0))
+        }
+
+        #[cfg(vec3a_f32)]
+        {
+            Self(self.0.max(other.0))
+        }
+    }
+
+    /// Returns the horizontal minimum of `self`'s elements.
+    ///
+    /// In other words, this computes `min(x, y, z)`.
+    #[inline]
+    pub fn min_element(self) -> f32 {
+        #[cfg(vec3a_sse2)]
+        unsafe {
+            let v = self.0;
+            let v = _mm_min_ps(v, _mm_shuffle_ps(v, v, 0b01_01_10_10));
+            let v = _mm_min_ps(v, _mm_shuffle_ps(v, v, 0b00_00_00_01));
+            _mm_cvtss_f32(v)
+        }
+
+        #[cfg(vec3a_f32)]
+        {
+            self.0.min_element()
+        }
+    }
+
+    /// Returns the horizontal maximum of `self`'s elements.
+    ///
+    /// In other words, this computes `max(x, y, z)`.
+    #[inline]
+    pub fn max_element(self) -> f32 {
+        #[cfg(vec3a_sse2)]
+        unsafe {
+            let v = self.0;
+            let v = _mm_max_ps(v, _mm_shuffle_ps(v, v, 0b00_00_10_10));
+            let v = _mm_max_ps(v, _mm_shuffle_ps(v, v, 0b00_00_00_01));
+            _mm_cvtss_f32(v)
+        }
+
+        #[cfg(vec3a_f32)]
+        {
+            self.0.max_element()
+        }
+    }
+
+    /// Performs a vertical `==` comparison between `self` and `other`,
+    /// returning a `Vec3AMask` of the results.
+    ///
+    /// In other words, this computes `[x1 == x2, y1 == y2, z1 == z2, w1 == w2]`.
+    #[inline]
+    pub fn cmpeq(self, other: Self) -> Vec3AMask {
+        #[cfg(vec3a_sse2)]
+        unsafe {
+            Vec3AMask(_mm_cmpeq_ps(self.0, other.0))
+        }
+
+        #[cfg(vec3a_f32)]
+        {
+            Vec3AMask(self.0.cmpeq(other.0))
+        }
+    }
+
+    /// Performs a vertical `!=` comparison between `self` and `other`,
+    /// returning a `Vec3AMask` of the results.
+    ///
+    /// In other words, this computes `[x1 != x2, y1 != y2, z1 != z2, w1 != w2]`.
+    #[inline]
+    pub fn cmpne(self, other: Self) -> Vec3AMask {
+        #[cfg(vec3a_sse2)]
+        unsafe {
+            Vec3AMask(_mm_cmpneq_ps(self.0, other.0))
+        }
+
+        #[cfg(vec3a_f32)]
+        {
+            Vec3AMask(self.0.cmpne(other.0))
+        }
+    }
+
+    /// Performs a vertical `>=` comparison between `self` and `other`,
+    /// returning a `Vec3AMask` of the results.
+    ///
+    /// In other words, this computes `[x1 >= x2, y1 >= y2, z1 >= z2, w1 >= w2]`.
+    #[inline]
+    pub fn cmpge(self, other: Self) -> Vec3AMask {
+        #[cfg(vec3a_sse2)]
+        unsafe {
+            Vec3AMask(_mm_cmpge_ps(self.0, other.0))
+        }
+
+        #[cfg(vec3a_f32)]
+        {
+            Vec3AMask(self.0.cmpge(other.0))
+        }
+    }
+
+    /// Performs a vertical `>` comparison between `self` and `other`,
+    /// returning a `Vec3AMask` of the results.
+    ///
+    /// In other words, this computes `[x1 > x2, y1 > y2, z1 > z2, w1 > w2]`.
+    #[inline]
+    pub fn cmpgt(self, other: Self) -> Vec3AMask {
+        #[cfg(vec3a_sse2)]
+        unsafe {
+            Vec3AMask(_mm_cmpgt_ps(self.0, other.0))
+        }
+
+        #[cfg(vec3a_f32)]
+        {
+            Vec3AMask(self.0.cmpgt(other.0))
+        }
+    }
+
+    /// Performs a vertical `<=` comparison between `self` and `other`,
+    /// returning a `Vec3AMask` of the results.
+    ///
+    /// In other words, this computes `[x1 <= x2, y1 <= y2, z1 <= z2, w1 <= w2]`.
+    #[inline]
+    pub fn cmple(self, other: Self) -> Vec3AMask {
+        #[cfg(vec3a_sse2)]
+        unsafe {
+            Vec3AMask(_mm_cmple_ps(self.0, other.0))
+        }
+
+        #[cfg(vec3a_f32)]
+        {
+            Vec3AMask(self.0.cmple(other.0))
+        }
+    }
+
+    /// Performs a vertical `<` comparison between `self` and `other`,
+    /// returning a `Vec3AMask` of the results.
+    ///
+    /// In other words, this computes `[x1 < x2, y1 < y2, z1 < z2, w1 < w2]`.
+    #[inline]
+    pub fn cmplt(self, other: Self) -> Vec3AMask {
+        #[cfg(vec3a_sse2)]
+        unsafe {
+            Vec3AMask(_mm_cmplt_ps(self.0, other.0))
+        }
+
+        #[cfg(vec3a_f32)]
+        {
+            Vec3AMask(self.0.cmplt(other.0))
+        }
+    }
+
+    /// Creates a new `Vec3A` from the first four values in `slice`.
+    ///
+    /// # Panics
+    ///
+    /// Panics if `slice` is less than three elements long.
+    #[inline]
+    pub fn from_slice_unaligned(slice: &[f32]) -> Self {
+        Self::new(slice[0], slice[1], slice[2])
+    }
+
+    /// Writes the elements of `self` to the first three elements in `slice`.
+    ///
+    /// # Panics
+    ///
+    /// Panics if `slice` is less than three elements long.
+    #[inline]
+    pub fn write_to_slice_unaligned(self, slice: &mut [f32]) {
+        let a = self.as_ref();
+        slice[0] = a[0];
+        slice[1] = a[1];
+        slice[2] = a[2];
+    }
+
+    /// Per element multiplication/addition of the three inputs: b + (self * a)
+    #[inline]
+    #[allow(dead_code)]
+    pub(crate) fn mul_add(self, a: Self, b: Self) -> Self {
+        #[cfg(vec3a_sse2)]
+        unsafe {
+            Self(_mm_add_ps(_mm_mul_ps(self.0, a.0), b.0))
+        }
+
+        #[cfg(vec3a_f32)]
+        {
+            Self(self.0.mul_add(a.0, b.0))
+        }
+    }
+
+    /// Returns a new `Vec3A` containing the absolute value of each element of the original
+    /// `Vec3A`.
+    #[inline]
+    pub fn abs(self) -> Self {
+        #[cfg(vec3a_sse2)]
+        unsafe {
+            Self(_mm_and_ps(
+                self.0,
+                _mm_castsi128_ps(_mm_set1_epi32(0x7f_ff_ff_ff)),
+            ))
+        }
+
+        #[cfg(vec3a_f32)]
+        {
+            Self(self.0.abs())
+        }
+    }
+
+    #[inline]
+    pub fn round(self) -> Self {
+        #[cfg(vec3a_sse2)]
+        unsafe {
+            use crate::f32::funcs::sse2::m128_round;
+            Self(m128_round(self.0))
+        }
+
+        #[cfg(vec3a_f32)]
+        {
+            Self(self.0.round())
+        }
+    }
+
+    #[inline]
+    pub fn floor(self) -> Self {
+        #[cfg(vec3a_sse2)]
+        unsafe {
+            use crate::f32::funcs::sse2::m128_floor;
+            Self(m128_floor(self.0))
+        }
+
+        #[cfg(vec3a_f32)]
+        {
+            Self(self.0.floor())
+        }
+    }
+
+    #[inline]
+    pub fn ceil(self) -> Self {
+        #[cfg(vec3a_sse2)]
+        unsafe {
+            use crate::f32::funcs::sse2::m128_ceil;
+            Self(m128_ceil(self.0))
+        }
+
+        #[cfg(vec3a_f32)]
+        {
+            Self(self.0.ceil())
+        }
+    }
+
+    /// Returns a new `Vec4` with elements representing the sign of `self`.
+    ///
+    /// - `1.0` if the number is positive, `+0.0` or `INFINITY`
+    /// - `-1.0` if the number is negative, `-0.0` or `NEG_INFINITY`
+    #[inline]
+    pub fn sign(self) -> Self {
+        let mask = self.cmpge(Self::zero());
+        mask.select(Self::splat(1.0), Self::splat(-1.0))
+    }
+
+    /// Computes the reciprocal `1.0/n` of each element, returning the
+    /// results in a new `Vec3A`.
+    #[inline]
+    pub fn reciprocal(self) -> Self {
+        // TODO: Optimize
+        Self::one() / self
+    }
+
+    /// Performs a linear interpolation between `self` and `other` based on
+    /// the value `s`.
+    ///
+    /// When `s` is `0.0`, the result will be equal to `self`.  When `s`
+    /// is `1.0`, the result will be equal to `other`.
+    #[inline]
+    pub fn lerp(self, other: Self, s: f32) -> Self {
+        self + ((other - self) * s)
+    }
+
+    /// Returns whether `self` of length `1.0` or not.
+    ///
+    /// Uses a precision threshold of `1e-6`.
+    #[inline]
+    pub fn is_normalized(self) -> bool {
+        is_normalized!(self)
+    }
+
+    /// Returns true if the absolute difference of all elements between `self`
+    /// and `other` is less than or equal to `max_abs_diff`.
+    ///
+    /// This can be used to compare if two `Vec3A`'s contain similar elements. It
+    /// works best when comparing with a known value. The `max_abs_diff` that
+    /// should be used used depends on the values being compared against.
+    ///
+    /// For more on floating point comparisons see
+    /// https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
+    #[inline]
+    pub fn abs_diff_eq(self, other: Self, max_abs_diff: f32) -> bool {
+        abs_diff_eq!(self, other, max_abs_diff)
+    }
+
+    /// Returns the angle between two vectors, in radians.
+    ///
+    /// The vectors do not need to be unit length, but this function does
+    /// perform a `sqrt`.
+    #[inline]
+    pub fn angle_between(self, other: Self) -> f32 {
+        crate::f32::funcs::scalar_acos(self.dot(other) / (self.dot(self) * other.dot(other)).sqrt())
+    }
+}
+
+impl AsRef<[f32; 3]> for Vec3A {
+    #[inline]
+    fn as_ref(&self) -> &[f32; 3] {
+        unsafe { &*(self as *const Vec3A as *const [f32; 3]) }
+    }
+}
+
+impl AsMut<[f32; 3]> for Vec3A {
+    #[inline]
+    fn as_mut(&mut self) -> &mut [f32; 3] {
+        unsafe { &mut *(self as *mut Vec3A as *mut [f32; 3]) }
+    }
+}
+
+impl fmt::Display for Vec3A {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        #[cfg(vec3a_sse2)]
+        {
+            let (x, y, z) = (*self).into();
+            write!(f, "[{}, {}, {}]", x, y, z)
+        }
+
+        #[cfg(vec3a_f32)]
+        {
+            self.0.fmt(f)
+        }
+    }
+}
+
+impl Div<Vec3A> for Vec3A {
+    type Output = Self;
+    #[inline]
+    fn div(self, other: Self) -> Self {
+        #[cfg(vec3a_sse2)]
+        unsafe {
+            Self(_mm_div_ps(self.0, other.0))
+        }
+
+        #[cfg(vec3a_f32)]
+        {
+            Self(self.0.div(other.0))
+        }
+    }
+}
+
+impl DivAssign<Vec3A> for Vec3A {
+    #[inline]
+    fn div_assign(&mut self, other: Self) {
+        #[cfg(vec3a_sse2)]
+        {
+            self.0 = unsafe { _mm_div_ps(self.0, other.0) };
+        }
+
+        #[cfg(vec3a_f32)]
+        {
+            self.0.div_assign(other.0);
+        }
+    }
+}
+
+impl Div<f32> for Vec3A {
+    type Output = Self;
+    #[inline]
+    fn div(self, other: f32) -> Self {
+        #[cfg(vec3a_sse2)]
+        unsafe {
+            Self(_mm_div_ps(self.0, _mm_set1_ps(other)))
+        }
+
+        #[cfg(vec3a_f32)]
+        {
+            Self(self.0.div(other))
+        }
+    }
+}
+
+impl DivAssign<f32> for Vec3A {
+    #[inline]
+    fn div_assign(&mut self, other: f32) {
+        #[cfg(vec3a_sse2)]
+        {
+            self.0 = unsafe { _mm_div_ps(self.0, _mm_set1_ps(other)) };
+        }
+
+        #[cfg(vec3a_f32)]
+        {
+            self.0.div_assign(other)
+        }
+    }
+}
+
+impl Div<Vec3A> for f32 {
+    type Output = Vec3A;
+    #[inline]
+    fn div(self, other: Vec3A) -> Vec3A {
+        #[cfg(vec3a_sse2)]
+        unsafe {
+            Vec3A(_mm_div_ps(_mm_set1_ps(self), other.0))
+        }
+
+        #[cfg(vec3a_f32)]
+        {
+            Vec3A(self.div(other.0))
+        }
+    }
+}
+
+impl Mul<Vec3A> for Vec3A {
+    type Output = Self;
+    #[inline]
+    fn mul(self, other: Self) -> Self {
+        #[cfg(vec3a_sse2)]
+        unsafe {
+            Self(_mm_mul_ps(self.0, other.0))
+        }
+
+        #[cfg(vec3a_f32)]
+        {
+            Self(self.0.mul(other.0))
+        }
+    }
+}
+
+impl MulAssign<Vec3A> for Vec3A {
+    #[inline]
+    fn mul_assign(&mut self, other: Self) {
+        #[cfg(vec3a_sse2)]
+        {
+            self.0 = unsafe { _mm_mul_ps(self.0, other.0) };
+        }
+
+        #[cfg(vec3a_f32)]
+        {
+            self.0.mul_assign(other.0);
+        }
+    }
+}
+
+impl Mul<f32> for Vec3A {
+    type Output = Self;
+    #[inline]
+    fn mul(self, other: f32) -> Self {
+        #[cfg(vec3a_sse2)]
+        unsafe {
+            Self(_mm_mul_ps(self.0, _mm_set1_ps(other)))
+        }
+
+        #[cfg(vec3a_f32)]
+        {
+            Self(self.0.mul(other))
+        }
+    }
+}
+
+impl MulAssign<f32> for Vec3A {
+    #[inline]
+    fn mul_assign(&mut self, other: f32) {
+        #[cfg(vec3a_sse2)]
+        {
+            self.0 = unsafe { _mm_mul_ps(self.0, _mm_set1_ps(other)) };
+        }
+
+        #[cfg(vec3a_f32)]
+        {
+            self.0.mul_assign(other);
+        }
+    }
+}
+
+impl Mul<Vec3A> for f32 {
+    type Output = Vec3A;
+    #[inline]
+    fn mul(self, other: Vec3A) -> Vec3A {
+        #[cfg(vec3a_sse2)]
+        unsafe {
+            Vec3A(_mm_mul_ps(_mm_set1_ps(self), other.0))
+        }
+
+        #[cfg(vec3a_f32)]
+        {
+            Vec3A(self.mul(other.0))
+        }
+    }
+}
+
+impl Add for Vec3A {
+    type Output = Self;
+    #[inline]
+    fn add(self, other: Self) -> Self {
+        #[cfg(vec3a_sse2)]
+        unsafe {
+            Self(_mm_add_ps(self.0, other.0))
+        }
+
+        #[cfg(vec3a_f32)]
+        {
+            Self(self.0.add(other.0))
+        }
+    }
+}
+
+impl AddAssign for Vec3A {
+    #[inline]
+    fn add_assign(&mut self, other: Self) {
+        #[cfg(vec3a_sse2)]
+        {
+            self.0 = unsafe { _mm_add_ps(self.0, other.0) };
+        }
+
+        #[cfg(vec3a_f32)]
+        {
+            self.0.add_assign(other.0);
+        }
+    }
+}
+
+impl Sub for Vec3A {
+    type Output = Self;
+    #[inline]
+    fn sub(self, other: Self) -> Self {
+        #[cfg(vec3a_sse2)]
+        unsafe {
+            Self(_mm_sub_ps(self.0, other.0))
+        }
+
+        #[cfg(vec3a_f32)]
+        {
+            Self(self.0.sub(other.0))
+        }
+    }
+}
+
+impl SubAssign for Vec3A {
+    #[inline]
+    fn sub_assign(&mut self, other: Self) {
+        #[cfg(vec3a_sse2)]
+        {
+            self.0 = unsafe { _mm_sub_ps(self.0, other.0) };
+        }
+
+        #[cfg(vec3a_f32)]
+        {
+            self.0.sub_assign(other.0);
+        }
+    }
+}
+
+impl Neg for Vec3A {
+    type Output = Self;
+    #[inline]
+    fn neg(self) -> Self {
+        #[cfg(vec3a_sse2)]
+        unsafe {
+            Self(_mm_sub_ps(_mm_set1_ps(0.0), self.0))
+        }
+
+        #[cfg(vec3a_f32)]
+        {
+            Self(self.0.neg())
+        }
+    }
+}
+
+impl Index<usize> for Vec3A {
+    type Output = f32;
+    #[inline]
+    fn index(&self, index: usize) -> &Self::Output {
+        &self.as_ref()[index]
+    }
+}
+
+impl IndexMut<usize> for Vec3A {
+    #[inline]
+    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
+        &mut self.as_mut()[index]
+    }
+}
+
+impl From<(f32, f32, f32)> for Vec3A {
+    #[inline]
+    fn from(t: (f32, f32, f32)) -> Self {
+        Self::new(t.0, t.1, t.2)
+    }
+}
+
+impl From<Vec3A> for (f32, f32, f32) {
+    #[inline]
+    fn from(v: Vec3A) -> Self {
+        #[cfg(vec3a_sse2)]
+        {
+            let mut out: MaybeUninit<Align16<(f32, f32, f32)>> = MaybeUninit::uninit();
+            unsafe {
+                // out is 16 bytes in size due to alignment
+                _mm_store_ps(out.as_mut_ptr() as *mut f32, v.0);
+                out.assume_init().0
+            }
+        }
+
+        #[cfg(vec3a_f32)]
+        {
+            v.0.into()
+        }
+    }
+}
+
+impl From<[f32; 3]> for Vec3A {
+    #[inline]
+    fn from(a: [f32; 3]) -> Self {
+        Self::new(a[0], a[1], a[2])
+    }
+}
+
+impl From<Vec3A> for [f32; 3] {
+    #[inline]
+    fn from(v: Vec3A) -> Self {
+        #[cfg(vec3a_sse2)]
+        {
+            let mut out: MaybeUninit<Align16<[f32; 3]>> = MaybeUninit::uninit();
+            unsafe {
+                // out is 16 bytes in size due to alignment
+                _mm_store_ps(out.as_mut_ptr() as *mut f32, v.0);
+                out.assume_init().0
+            }
+        }
+
+        #[cfg(vec3a_f32)]
+        {
+            v.0.into()
+        }
+    }
+}
+
+impl From<Vec3> for Vec3A {
+    #[inline]
+    fn from(v: Vec3) -> Self {
+        Vec3A::new(v.0, v.1, v.2)
+    }
+}
+
+#[test]
+fn test_vec3_private() {
+    assert_eq!(
+        vec3a(1.0, 1.0, 1.0).mul_add(vec3a(0.5, 2.0, -4.0), vec3a(-1.0, -1.0, -1.0)),
+        vec3a(-0.5, 1.0, -5.0)
+    );
+    assert_eq!(vec3a(1.0, 2.0, 3.0).dup_x(), vec3a(1.0, 1.0, 1.0));
+    assert_eq!(vec3a(1.0, 2.0, 3.0).dup_y(), vec3a(2.0, 2.0, 2.0));
+    assert_eq!(vec3a(1.0, 2.0, 3.0).dup_z(), vec3a(3.0, 3.0, 3.0));
+}
diff --git a/src/f32/vec3a_mask.rs b/src/f32/vec3a_mask.rs
new file mode 100644
index 0000000..a149df1
--- /dev/null
+++ b/src/f32/vec3a_mask.rs
@@ -0,0 +1,296 @@
+use super::Vec3A;
+#[cfg(vec3a_f32)]
+use super::Vec3Mask;
+use core::{fmt, ops::*};
+
+#[cfg(all(vec3a_sse2, target_arch = "x86"))]
+use core::arch::x86::*;
+#[cfg(all(vec3a_sse2, target_arch = "x86_64"))]
+use core::arch::x86_64::*;
+#[cfg(vec3a_sse2)]
+use core::{cmp::Ordering, hash};
+
+/// A 3-dimensional vector mask.
+///
+/// This type is typically created by comparison methods on `Vec3A`.  It is
+/// essentially a vector of three boolean values.
+#[cfg(vec3a_sse2)]
+#[derive(Clone, Copy)]
+#[repr(C)]
+pub struct Vec3AMask(pub(crate) __m128);
+
+/// A 3-dimensional vector mask.
+#[cfg(vec3a_f32)]
+#[derive(Clone, Copy, Default, PartialEq, Eq, Ord, PartialOrd, Hash)]
+#[repr(align(16), C)]
+pub struct Vec3AMask(pub(crate) Vec3Mask);
+
+#[cfg(vec3a_sse2)]
+impl Default for Vec3AMask {
+    #[inline]
+    fn default() -> Self {
+        unsafe { Self(_mm_setzero_ps()) }
+    }
+}
+
+#[cfg(vec3a_sse2)]
+impl PartialEq for Vec3AMask {
+    #[inline]
+    fn eq(&self, other: &Self) -> bool {
+        self.as_ref().eq(other.as_ref())
+    }
+}
+
+#[cfg(vec3a_sse2)]
+impl Eq for Vec3AMask {}
+
+#[cfg(vec3a_sse2)]
+impl Ord for Vec3AMask {
+    #[inline]
+    fn cmp(&self, other: &Self) -> Ordering {
+        self.as_ref().cmp(other.as_ref())
+    }
+}
+
+#[cfg(vec3a_sse2)]
+impl PartialOrd for Vec3AMask {
+    #[inline]
+    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+        Some(self.cmp(other))
+    }
+}
+
+#[cfg(vec3a_sse2)]
+impl hash::Hash for Vec3AMask {
+    #[inline]
+    fn hash<H: hash::Hasher>(&self, state: &mut H) {
+        self.as_ref().hash(state);
+    }
+}
+
+impl Vec3AMask {
+    /// Creates a new `Vec3AMask`.
+    #[inline]
+    pub fn new(x: bool, y: bool, z: bool) -> Self {
+        // A SSE2 mask can be any bit pattern but for the `Vec3AMask` implementation of select we
+        // expect either 0 or 0xff_ff_ff_ff. This should be a safe assumption as this type can only
+        // be created via this function or by `Vec3A` methods.
+
+        #[cfg(vec3a_sse2)]
+        unsafe {
+            const MASK: [u32; 2] = [0, 0xff_ff_ff_ff];
+            Self(_mm_set_ps(
+                f32::from_bits(MASK[z as usize]),
+                f32::from_bits(MASK[z as usize]),
+                f32::from_bits(MASK[y as usize]),
+                f32::from_bits(MASK[x as usize]),
+            ))
+        }
+
+        #[cfg(vec3a_f32)]
+        {
+            Self(Vec3Mask::new(x, y, z))
+        }
+    }
+
+    /// Returns a bitmask with the lowest three bits set from the elements of
+    /// the `Vec3AMask`.
+    ///
+    /// A true element results in a `1` bit and a false element in a `0` bit.
+    /// Element `x` goes into the first lowest bit, element `y` into the
+    /// second, etc.
+    #[inline]
+    pub fn bitmask(&self) -> u32 {
+        // _mm_movemask_ps only checks the most significant bit of the u32 is
+        // true, so we replicate that here with the non-SSE2 version.
+
+        #[cfg(vec3a_sse2)]
+        unsafe {
+            (_mm_movemask_ps(self.0) as u32) & 0x7
+        }
+
+        #[cfg(vec3a_f32)]
+        {
+            self.0.bitmask()
+        }
+    }
+
+    /// Returns true if any of the elements are true, false otherwise.
+    ///
+    /// In other words: `x || y || z`.
+    #[inline]
+    pub fn any(&self) -> bool {
+        #[cfg(vec3a_sse2)]
+        unsafe {
+            (_mm_movemask_ps(self.0) & 0x7) != 0
+        }
+
+        #[cfg(vec3a_f32)]
+        {
+            self.0.any()
+        }
+    }
+
+    /// Returns true if all the elements are true, false otherwise.
+    ///
+    /// In other words: `x && y && z`.
+    #[inline]
+    pub fn all(&self) -> bool {
+        #[cfg(vec3a_sse2)]
+        unsafe {
+            (_mm_movemask_ps(self.0) & 0x7) == 0x7
+        }
+
+        #[cfg(vec3a_f32)]
+        {
+            self.0.all()
+        }
+    }
+
+    /// Creates a new `Vec3A` from the elements in `if_true` and `if_false`,
+    /// selecting which to use for each element based on the `Vec3AMask`.
+    ///
+    /// A true element in the mask uses the corresponding element from
+    /// `if_true`, and false uses the element from `if_false`.
+    #[inline]
+    pub fn select(self, if_true: Vec3A, if_false: Vec3A) -> Vec3A {
+        // We are assuming that the mask values are either 0 or 0xff_ff_ff_ff for the SSE2 and f32
+        // to behave the same here.
+
+        #[cfg(vec3a_sse2)]
+        unsafe {
+            Vec3A(_mm_or_ps(
+                _mm_andnot_ps(self.0, if_false.0),
+                _mm_and_ps(if_true.0, self.0),
+            ))
+        }
+
+        #[cfg(vec3a_f32)]
+        {
+            Vec3A(self.0.select(if_true.0, if_false.0))
+        }
+    }
+}
+
+impl BitAnd for Vec3AMask {
+    type Output = Self;
+    #[inline]
+    fn bitand(self, other: Self) -> Self {
+        #[cfg(vec3a_sse2)]
+        unsafe {
+            Self(_mm_and_ps(self.0, other.0))
+        }
+
+        #[cfg(vec3a_f32)]
+        {
+            Self(self.0.bitand(other.0))
+        }
+    }
+}
+
+impl BitAndAssign for Vec3AMask {
+    #[inline]
+    fn bitand_assign(&mut self, other: Self) {
+        #[cfg(vec3a_sse2)]
+        {
+            self.0 = unsafe { _mm_and_ps(self.0, other.0) };
+        }
+
+        #[cfg(vec3a_f32)]
+        {
+            self.0.bitand_assign(other.0);
+        }
+    }
+}
+
+impl BitOr for Vec3AMask {
+    type Output = Self;
+    #[inline]
+    fn bitor(self, other: Self) -> Self {
+        #[cfg(vec3a_sse2)]
+        unsafe {
+            Self(_mm_or_ps(self.0, other.0))
+        }
+
+        #[cfg(vec3a_f32)]
+        {
+            Self(self.0.bitor(other.0))
+        }
+    }
+}
+
+impl BitOrAssign for Vec3AMask {
+    #[inline]
+    fn bitor_assign(&mut self, other: Self) {
+        #[cfg(vec3a_sse2)]
+        {
+            self.0 = unsafe { _mm_or_ps(self.0, other.0) };
+        }
+
+        #[cfg(vec3a_f32)]
+        {
+            self.0.bitor_assign(other.0);
+        }
+    }
+}
+
+impl Not for Vec3AMask {
+    type Output = Self;
+    #[inline]
+    fn not(self) -> Self {
+        #[cfg(vec3a_sse2)]
+        unsafe {
+            Self(_mm_andnot_ps(
+                self.0,
+                _mm_set_ps1(f32::from_bits(0xff_ff_ff_ff)),
+            ))
+        }
+
+        #[cfg(vec3a_f32)]
+        {
+            Self(self.0.not())
+        }
+    }
+}
+
+impl fmt::Debug for Vec3AMask {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        #[cfg(vec3a_sse2)]
+        {
+            let arr = self.as_ref();
+            write!(f, "Vec3AMask({:#x}, {:#x}, {:#x})", arr[0], arr[1], arr[2])
+        }
+
+        #[cfg(vec3a_f32)]
+        {
+            write!(
+                f,
+                "Vec3AMask({:#x}, {:#x}, {:#x})",
+                (self.0).0,
+                (self.0).1,
+                (self.0).2
+            )
+        }
+    }
+}
+
+impl fmt::Display for Vec3AMask {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        let arr = self.as_ref();
+        write!(f, "[{}, {}, {}]", arr[0] != 0, arr[1] != 0, arr[2] != 0,)
+    }
+}
+
+impl From<Vec3AMask> for [u32; 3] {
+    #[inline]
+    fn from(mask: Vec3AMask) -> Self {
+        *mask.as_ref()
+    }
+}
+
+impl AsRef<[u32; 3]> for Vec3AMask {
+    #[inline]
+    fn as_ref(&self) -> &[u32; 3] {
+        unsafe { &*(self as *const Self as *const [u32; 3]) }
+    }
+}
diff --git a/src/f32/vec4.rs b/src/f32/vec4.rs
index 7c5847f..cf369d1 100644
--- a/src/f32/vec4.rs
+++ b/src/f32/vec4.rs
@@ -1,29 +1,29 @@
-use super::{Vec3, Vec4Mask};
+use super::{Vec3A, Vec4Mask};
 use core::{fmt, ops::*};
 
-#[cfg(all(vec4sse2, target_arch = "x86"))]
+#[cfg(all(vec4_sse2, target_arch = "x86"))]
 use core::arch::x86::*;
-#[cfg(all(vec4sse2, target_arch = "x86_64"))]
+#[cfg(all(vec4_sse2, target_arch = "x86_64"))]
 use core::arch::x86_64::*;
 
-#[cfg(vec4sse2)]
+#[cfg(vec4_sse2)]
 use crate::Align16;
-#[cfg(vec4sse2)]
+#[cfg(vec4_sse2)]
 use core::{cmp::Ordering, f32, mem::MaybeUninit};
 
-#[cfg(vec4sse2)]
+#[cfg(vec4_sse2)]
 pub(crate) const X_AXIS: Align16<[f32; 4]> = Align16([1.0, 0.0, 0.0, 0.0]);
-#[cfg(vec4sse2)]
+#[cfg(vec4_sse2)]
 pub(crate) const Y_AXIS: Align16<[f32; 4]> = Align16([0.0, 1.0, 0.0, 0.0]);
-#[cfg(vec4sse2)]
+#[cfg(vec4_sse2)]
 pub(crate) const Z_AXIS: Align16<[f32; 4]> = Align16([0.0, 0.0, 1.0, 0.0]);
-#[cfg(vec4sse2)]
+#[cfg(vec4_sse2)]
 pub(crate) const W_AXIS: Align16<[f32; 4]> = Align16([0.0, 0.0, 0.0, 1.0]);
 
 /// A 4-dimensional vector.
 ///
 /// This type is 16 byte aligned.
-#[cfg(vec4sse2)]
+#[cfg(vec4_sse2)]
 #[derive(Clone, Copy, Debug)]
 #[repr(C)]
 pub struct Vec4(pub(crate) __m128);
@@ -31,10 +31,10 @@ pub struct Vec4(pub(crate) __m128);
 /// A 4-dimensional vector.
 ///
 /// This type is 16 byte aligned unless the `scalar-math` feature is enabed.
-#[cfg(vec4f32)]
+#[cfg(vec4_f32)]
 #[derive(Clone, Copy, PartialEq, PartialOrd, Debug, Default)]
 // if compiling with simd enabled assume alignment needs to match the simd type
-#[cfg_attr(vec4f32_align16, repr(align(16)))]
+#[cfg_attr(vec4_f32_align16, repr(align(16)))]
 #[repr(C)]
 pub struct Vec4(
     pub(crate) f32,
@@ -43,7 +43,7 @@ pub struct Vec4(
     pub(crate) f32,
 );
 
-#[cfg(vec4sse2)]
+#[cfg(vec4_sse2)]
 impl Default for Vec4 {
     #[inline]
     fn default() -> Self {
@@ -51,7 +51,7 @@ impl Default for Vec4 {
     }
 }
 
-#[cfg(vec4sse2)]
+#[cfg(vec4_sse2)]
 impl PartialEq for Vec4 {
     #[inline]
     fn eq(&self, other: &Self) -> bool {
@@ -59,7 +59,7 @@ impl PartialEq for Vec4 {
     }
 }
 
-#[cfg(vec4sse2)]
+#[cfg(vec4_sse2)]
 impl PartialOrd for Vec4 {
     #[inline]
     fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
@@ -67,17 +67,16 @@ impl PartialOrd for Vec4 {
     }
 }
 
-#[cfg(vec4sse2)]
+#[cfg(vec4_sse2)]
 impl From<Vec4> for __m128 {
     // TODO: write test
-    #[cfg_attr(tarpaulin, skip)]
     #[inline]
     fn from(t: Vec4) -> Self {
         t.0
     }
 }
 
-#[cfg(vec4sse2)]
+#[cfg(vec4_sse2)]
 impl From<__m128> for Vec4 {
     #[inline]
     fn from(t: __m128) -> Self {
@@ -94,11 +93,11 @@ impl Vec4 {
     /// Creates a new `Vec4`.
     #[inline]
     pub fn new(x: f32, y: f32, z: f32, w: f32) -> Self {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         unsafe {
             Self(_mm_set_ps(w, z, y, x))
         }
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             Self(x, y, z, w)
         }
@@ -107,12 +106,12 @@ impl Vec4 {
     /// Creates a new `Vec4` with all elements set to `0.0`.
     #[inline]
     pub fn zero() -> Self {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         unsafe {
             Self(_mm_setzero_ps())
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             Self(0.0, 0.0, 0.0, 0.0)
         }
@@ -121,12 +120,12 @@ impl Vec4 {
     /// Creates a new `Vec4` with all elements set to `1.0`.
     #[inline]
     pub fn one() -> Self {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         unsafe {
             Self(_mm_set1_ps(1.0))
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             Self(1.0, 1.0, 1.0, 1.0)
         }
@@ -135,12 +134,12 @@ impl Vec4 {
     /// Creates a new `Vec4` with values `[x: 1.0, y: 0.0, z: 0.0, w: 0.0]`.
     #[inline]
     pub fn unit_x() -> Self {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         unsafe {
             Self(_mm_load_ps(X_AXIS.0.as_ptr()))
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             Self(1.0, 0.0, 0.0, 0.0)
         }
@@ -149,12 +148,12 @@ impl Vec4 {
     /// Creates a new `Vec4` with values `[x: 0.0, y: 1.0, z: 0.0, w: 0.0]`.
     #[inline]
     pub fn unit_y() -> Self {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         unsafe {
             Self(_mm_load_ps(Y_AXIS.0.as_ptr()))
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             Self(0.0, 1.0, 0.0, 0.0)
         }
@@ -163,12 +162,12 @@ impl Vec4 {
     /// Creates a new `Vec4` with values `[x: 0.0, y: 0.0, z: 1.0, w: 0.0]`.
     #[inline]
     pub fn unit_z() -> Self {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         unsafe {
             Self(_mm_load_ps(Z_AXIS.0.as_ptr()))
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             Self(0.0, 0.0, 1.0, 0.0)
         }
@@ -177,12 +176,12 @@ impl Vec4 {
     /// Creates a new `Vec4` with values `[x: 0.0, y: 0.0, z: 0.0, w: 1.0]`.
     #[inline]
     pub fn unit_w() -> Self {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         unsafe {
             Self(_mm_load_ps(W_AXIS.0.as_ptr()))
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             Self(0.0, 0.0, 0.0, 1.0)
         }
@@ -191,47 +190,44 @@ impl Vec4 {
     /// Creates a new `Vec4` with all elements set to `v`.
     #[inline]
     pub fn splat(v: f32) -> Self {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         unsafe {
             Self(_mm_set_ps1(v))
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             Self(v, v, v, v)
         }
     }
 
-    /// Creates a `Vec3` from the first three elements of `self`,
-    /// removing `w`.
+    /// Creates a `Vec3A` from the first three elements of `self`, removing `w`.
+    ///
+    /// Note that a `Vec3A` is returned as both `Vec4` and `Vec3A` use SIMD storage so the
+    /// conversion is very cheap. If you need a `Vec3` you can convert from `Vec3A` using `.into()`
+    /// or `Vec3::from`.
     #[inline]
-    pub fn truncate(self) -> Vec3 {
-        #[cfg(all(vec4sse2, vec3sse2))]
-        {
-            self.0.into()
-        }
-
-        #[cfg(all(vec4sse2, not(vec3sse2)))]
+    pub fn truncate(self) -> Vec3A {
+        #[cfg(all(vec4_sse2, vec3a_sse2))]
         {
-            let (x, y, z, _) = self.into();
-            Vec3::new(x, y, z)
+            Vec3A(self.0)
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
-            Vec3::new(self.0, self.1, self.2)
+            Vec3A::new(self.0, self.1, self.2)
         }
     }
 
     /// Returns element `x`.
     #[inline]
     pub fn x(self) -> f32 {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         unsafe {
             _mm_cvtss_f32(self.0)
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             self.0
         }
@@ -240,12 +236,12 @@ impl Vec4 {
     /// Returns element `y`.
     #[inline]
     pub fn y(self) -> f32 {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         unsafe {
             _mm_cvtss_f32(_mm_shuffle_ps(self.0, self.0, 0b01_01_01_01))
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             self.1
         }
@@ -254,12 +250,12 @@ impl Vec4 {
     /// Returns element `z`.
     #[inline]
     pub fn z(self) -> f32 {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         unsafe {
             _mm_cvtss_f32(_mm_shuffle_ps(self.0, self.0, 0b10_10_10_10))
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             self.2
         }
@@ -268,12 +264,12 @@ impl Vec4 {
     /// Returns element `w`.
     #[inline]
     pub fn w(self) -> f32 {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         unsafe {
             _mm_cvtss_f32(_mm_shuffle_ps(self.0, self.0, 0b11_11_11_11))
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             self.3
         }
@@ -282,12 +278,12 @@ impl Vec4 {
     /// Returns a mutable reference to element `x`.
     #[inline]
     pub fn x_mut(&mut self) -> &mut f32 {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         unsafe {
             &mut *(self as *mut Self as *mut f32)
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             &mut self.0
         }
@@ -296,12 +292,12 @@ impl Vec4 {
     /// Returns a mutable reference to element `y`.
     #[inline]
     pub fn y_mut(&mut self) -> &mut f32 {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         unsafe {
             &mut *(self as *mut Self as *mut f32).offset(1)
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             &mut self.1
         }
@@ -310,12 +306,12 @@ impl Vec4 {
     /// Returns a mutable reference to element `z`.
     #[inline]
     pub fn z_mut(&mut self) -> &mut f32 {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         unsafe {
             &mut *(self as *mut Self as *mut f32).offset(2)
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             &mut self.2
         }
@@ -324,12 +320,12 @@ impl Vec4 {
     /// Returns a mutable reference to element `w`.
     #[inline]
     pub fn w_mut(&mut self) -> &mut f32 {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         unsafe {
             &mut *(self as *mut Self as *mut f32).offset(3)
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             &mut self.3
         }
@@ -338,12 +334,12 @@ impl Vec4 {
     /// Sets element `x`.
     #[inline]
     pub fn set_x(&mut self, x: f32) {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         unsafe {
             self.0 = _mm_move_ss(self.0, _mm_set_ss(x));
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             self.0 = x;
         }
@@ -352,14 +348,14 @@ impl Vec4 {
     /// Sets element `y`.
     #[inline]
     pub fn set_y(&mut self, y: f32) {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         unsafe {
             let mut t = _mm_move_ss(self.0, _mm_set_ss(y));
             t = _mm_shuffle_ps(t, t, 0b11_10_00_00);
             self.0 = _mm_move_ss(t, self.0);
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             self.1 = y;
         }
@@ -368,14 +364,14 @@ impl Vec4 {
     /// Sets element `z`.
     #[inline]
     pub fn set_z(&mut self, z: f32) {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         unsafe {
             let mut t = _mm_move_ss(self.0, _mm_set_ss(z));
             t = _mm_shuffle_ps(t, t, 0b11_00_01_00);
             self.0 = _mm_move_ss(t, self.0);
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             self.2 = z;
         }
@@ -384,14 +380,14 @@ impl Vec4 {
     /// Sets element `w`.
     #[inline]
     pub fn set_w(&mut self, w: f32) {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         unsafe {
             let mut t = _mm_move_ss(self.0, _mm_set_ss(w));
             t = _mm_shuffle_ps(t, t, 0b00_10_01_00);
             self.0 = _mm_move_ss(t, self.0);
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             self.3 = w;
         }
@@ -400,12 +396,12 @@ impl Vec4 {
     /// Returns a `Vec4` with all elements set to the value of element `x`.
     #[inline]
     pub(crate) fn dup_x(self) -> Self {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         unsafe {
             Self(_mm_shuffle_ps(self.0, self.0, 0b00_00_00_00))
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             Self(self.0, self.0, self.0, self.0)
         }
@@ -414,12 +410,12 @@ impl Vec4 {
     /// Returns a `Vec4` with all elements set to the value of element `y`.
     #[inline]
     pub(crate) fn dup_y(self) -> Self {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         unsafe {
             Self(_mm_shuffle_ps(self.0, self.0, 0b01_01_01_01))
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             Self(self.1, self.1, self.1, self.1)
         }
@@ -428,12 +424,12 @@ impl Vec4 {
     /// Returns a `Vec4` with all elements set to the value of element `z`.
     #[inline]
     pub(crate) fn dup_z(self) -> Self {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         unsafe {
             Self(_mm_shuffle_ps(self.0, self.0, 0b10_10_10_10))
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             Self(self.2, self.2, self.2, self.2)
         }
@@ -442,19 +438,19 @@ impl Vec4 {
     /// Returns a `Vec4` with all elements set to the value of element `w`.
     #[inline]
     pub(crate) fn dup_w(self) -> Self {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         unsafe {
             Self(_mm_shuffle_ps(self.0, self.0, 0b11_11_11_11))
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             Self(self.3, self.3, self.3, self.3)
         }
     }
 
     /// Calculates the Vec4 dot product and returns answer in x lane of __m128.
-    #[cfg(vec4sse2)]
+    #[cfg(vec4_sse2)]
     #[inline]
     unsafe fn dot_as_m128(self, other: Self) -> __m128 {
         let x2_y2_z2_w2 = _mm_mul_ps(self.0, other.0);
@@ -465,7 +461,7 @@ impl Vec4 {
     }
 
     /// Returns Vec4 dot in all lanes of Vec4
-    #[cfg(vec4sse2)]
+    #[cfg(vec4_sse2)]
     #[inline]
     pub(crate) fn dot_as_vec4(self, other: Self) -> Self {
         unsafe {
@@ -477,12 +473,12 @@ impl Vec4 {
     /// Computes the 4D dot product of `self` and `other`.
     #[inline]
     pub fn dot(self, other: Self) -> f32 {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         unsafe {
             _mm_cvtss_f32(self.dot_as_m128(other))
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             (self.0 * other.0) + (self.1 * other.1) + (self.2 * other.2) + (self.3 * other.3)
         }
@@ -491,13 +487,13 @@ impl Vec4 {
     /// Computes the 4D length of `self`.
     #[inline]
     pub fn length(self) -> f32 {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         {
             let dot = self.dot_as_vec4(self);
             unsafe { _mm_cvtss_f32(_mm_sqrt_ps(dot.0)) }
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             self.dot(self).sqrt()
         }
@@ -517,7 +513,7 @@ impl Vec4 {
     /// For valid results, `self` must _not_ be of length zero.
     #[inline]
     pub fn length_reciprocal(self) -> f32 {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         {
             let dot = self.dot_as_vec4(self);
             unsafe {
@@ -526,7 +522,7 @@ impl Vec4 {
             }
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             1.0 / self.length()
         }
@@ -537,13 +533,13 @@ impl Vec4 {
     /// For valid results, `self` must _not_ be of length zero.
     #[inline]
     pub fn normalize(self) -> Self {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         {
             let dot = self.dot_as_vec4(self);
             unsafe { Self(_mm_div_ps(self.0, _mm_sqrt_ps(dot.0))) }
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             self * self.length_reciprocal()
         }
@@ -556,12 +552,12 @@ impl Vec4 {
     /// taking the minimum of each element individually.
     #[inline]
     pub fn min(self, other: Self) -> Self {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         unsafe {
             Self(_mm_min_ps(self.0, other.0))
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             Self(
                 self.0.min(other.0),
@@ -579,12 +575,12 @@ impl Vec4 {
     /// taking the maximum of each element individually.
     #[inline]
     pub fn max(self, other: Self) -> Self {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         unsafe {
             Self(_mm_max_ps(self.0, other.0))
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             Self(
                 self.0.max(other.0),
@@ -600,7 +596,7 @@ impl Vec4 {
     /// In other words, this computes `min(x, y, z, w)`.
     #[inline]
     pub fn min_element(self) -> f32 {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         unsafe {
             let v = self.0;
             let v = _mm_min_ps(v, _mm_shuffle_ps(v, v, 0b00_00_11_10));
@@ -608,7 +604,7 @@ impl Vec4 {
             _mm_cvtss_f32(v)
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             self.0.min(self.1.min(self.2.min(self.3)))
         }
@@ -619,7 +615,7 @@ impl Vec4 {
     /// In other words, this computes `max(x, y, z, w)`.
     #[inline]
     pub fn max_element(self) -> f32 {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         unsafe {
             let v = self.0;
             let v = _mm_max_ps(v, _mm_shuffle_ps(v, v, 0b00_00_11_10));
@@ -627,7 +623,7 @@ impl Vec4 {
             _mm_cvtss_f32(v)
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             self.0.max(self.1.max(self.2.min(self.3)))
         }
@@ -639,12 +635,12 @@ impl Vec4 {
     /// In other words, this computes `[x1 == x2, y1 == y2, z1 == z2, w1 == w2]`.
     #[inline]
     pub fn cmpeq(self, other: Self) -> Vec4Mask {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         unsafe {
             Vec4Mask(_mm_cmpeq_ps(self.0, other.0))
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             Vec4Mask::new(
                 self.0.eq(&other.0),
@@ -661,12 +657,12 @@ impl Vec4 {
     /// In other words, this computes `[x1 != x2, y1 != y2, z1 != z2, w1 != w2]`.
     #[inline]
     pub fn cmpne(self, other: Self) -> Vec4Mask {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         unsafe {
             Vec4Mask(_mm_cmpneq_ps(self.0, other.0))
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             Vec4Mask::new(
                 self.0.ne(&other.0),
@@ -683,12 +679,12 @@ impl Vec4 {
     /// In other words, this computes `[x1 >= x2, y1 >= y2, z1 >= z2, w1 >= w2]`.
     #[inline]
     pub fn cmpge(self, other: Self) -> Vec4Mask {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         unsafe {
             Vec4Mask(_mm_cmpge_ps(self.0, other.0))
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             Vec4Mask::new(
                 self.0.ge(&other.0),
@@ -705,12 +701,12 @@ impl Vec4 {
     /// In other words, this computes `[x1 > x2, y1 > y2, z1 > z2, w1 > w2]`.
     #[inline]
     pub fn cmpgt(self, other: Self) -> Vec4Mask {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         unsafe {
             Vec4Mask(_mm_cmpgt_ps(self.0, other.0))
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             Vec4Mask::new(
                 self.0.gt(&other.0),
@@ -727,12 +723,12 @@ impl Vec4 {
     /// In other words, this computes `[x1 <= x2, y1 <= y2, z1 <= z2, w1 <= w2]`.
     #[inline]
     pub fn cmple(self, other: Self) -> Vec4Mask {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         unsafe {
             Vec4Mask(_mm_cmple_ps(self.0, other.0))
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             Vec4Mask::new(
                 self.0.le(&other.0),
@@ -749,12 +745,12 @@ impl Vec4 {
     /// In other words, this computes `[x1 < x2, y1 < y2, z1 < z2, w1 < w2]`.
     #[inline]
     pub fn cmplt(self, other: Self) -> Vec4Mask {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         unsafe {
             Vec4Mask(_mm_cmplt_ps(self.0, other.0))
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             Vec4Mask::new(
                 self.0.lt(&other.0),
@@ -772,13 +768,13 @@ impl Vec4 {
     /// Panics if `slice` is less than four elements long.
     #[inline]
     pub fn from_slice_unaligned(slice: &[f32]) -> Self {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         {
             assert!(slice.len() >= 4);
             unsafe { Self(_mm_loadu_ps(slice.as_ptr())) }
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             Self(slice[0], slice[1], slice[2], slice[3])
         }
@@ -791,13 +787,13 @@ impl Vec4 {
     /// Panics if `slice` is less than four elements long.
     #[inline]
     pub fn write_to_slice_unaligned(self, slice: &mut [f32]) {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         unsafe {
             assert!(slice.len() >= 4);
             _mm_storeu_ps(slice.as_mut_ptr(), self.0);
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             slice[0] = self.0;
             slice[1] = self.1;
@@ -809,12 +805,12 @@ impl Vec4 {
     /// Per element multiplication/addition of the three inputs: b + (self * a)
     #[inline]
     pub(crate) fn mul_add(self, a: Self, b: Self) -> Self {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         unsafe {
             Self(_mm_add_ps(_mm_mul_ps(self.0, a.0), b.0))
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             Self(
                 (self.0 * a.0) + b.0,
@@ -829,7 +825,7 @@ impl Vec4 {
     /// `Vec4`.
     #[inline]
     pub fn abs(self) -> Self {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         unsafe {
             Self(_mm_and_ps(
                 self.0,
@@ -837,7 +833,7 @@ impl Vec4 {
             ))
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             Self(self.0.abs(), self.1.abs(), self.2.abs(), self.3.abs())
         }
@@ -845,13 +841,13 @@ impl Vec4 {
 
     #[inline]
     pub fn round(self) -> Self {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         unsafe {
             use crate::f32::funcs::sse2::m128_round;
             Self(m128_round(self.0))
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             Self(
                 self.0.round(),
@@ -864,13 +860,13 @@ impl Vec4 {
 
     #[inline]
     pub fn floor(self) -> Self {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         unsafe {
             use crate::f32::funcs::sse2::m128_floor;
             Self(m128_floor(self.0))
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             Self(
                 self.0.floor(),
@@ -883,13 +879,13 @@ impl Vec4 {
 
     #[inline]
     pub fn ceil(self) -> Self {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         unsafe {
             use crate::f32::funcs::sse2::m128_ceil;
             Self(m128_ceil(self.0))
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             Self(self.0.ceil(), self.1.ceil(), self.2.ceil(), self.3.ceil())
         }
@@ -901,13 +897,13 @@ impl Vec4 {
     /// - `-1.0` if the number is negative, `-0.0` or `NEG_INFINITY`
     #[inline]
     pub fn sign(self) -> Self {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         {
             let mask = self.cmpge(Self::zero());
             mask.select(Self::splat(1.0), Self::splat(-1.0))
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             Self(
                 self.0.signum(),
@@ -975,13 +971,13 @@ impl AsMut<[f32; 4]> for Vec4 {
 
 impl fmt::Display for Vec4 {
     fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         {
             let (x, y, z, w) = (*self).into();
             write!(fmt, "[{}, {}, {}, {}]", x, y, z, w)
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             write!(fmt, "[{}, {}, {}, {}]", self.0, self.1, self.2, self.3)
         }
@@ -992,12 +988,12 @@ impl Div<Vec4> for Vec4 {
     type Output = Self;
     #[inline]
     fn div(self, other: Self) -> Self {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         unsafe {
             Self(_mm_div_ps(self.0, other.0))
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             Self(
                 self.0 / other.0,
@@ -1012,12 +1008,12 @@ impl Div<Vec4> for Vec4 {
 impl DivAssign<Vec4> for Vec4 {
     #[inline]
     fn div_assign(&mut self, other: Self) {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         {
             self.0 = unsafe { _mm_div_ps(self.0, other.0) };
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             self.0 /= other.0;
             self.1 /= other.1;
@@ -1031,12 +1027,12 @@ impl Div<f32> for Vec4 {
     type Output = Self;
     #[inline]
     fn div(self, other: f32) -> Self {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         unsafe {
             Self(_mm_div_ps(self.0, _mm_set1_ps(other)))
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             Self(
                 self.0 / other,
@@ -1051,12 +1047,12 @@ impl Div<f32> for Vec4 {
 impl DivAssign<f32> for Vec4 {
     #[inline]
     fn div_assign(&mut self, other: f32) {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         {
             self.0 = unsafe { _mm_div_ps(self.0, _mm_set1_ps(other)) };
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             self.0 /= other;
             self.1 /= other;
@@ -1066,16 +1062,37 @@ impl DivAssign<f32> for Vec4 {
     }
 }
 
+impl Div<Vec4> for f32 {
+    type Output = Vec4;
+    #[inline]
+    fn div(self, other: Vec4) -> Vec4 {
+        #[cfg(vec4_sse2)]
+        unsafe {
+            Vec4(_mm_div_ps(_mm_set1_ps(self), other.0))
+        }
+
+        #[cfg(vec4_f32)]
+        {
+            Vec4(
+                self / other.0,
+                self / other.1,
+                self / other.2,
+                self / other.3,
+            )
+        }
+    }
+}
+
 impl Mul<Vec4> for Vec4 {
     type Output = Self;
     #[inline]
     fn mul(self, other: Self) -> Self {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         unsafe {
             Self(_mm_mul_ps(self.0, other.0))
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             Self(
                 self.0 * other.0,
@@ -1090,12 +1107,12 @@ impl Mul<Vec4> for Vec4 {
 impl MulAssign<Vec4> for Vec4 {
     #[inline]
     fn mul_assign(&mut self, other: Self) {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         {
             self.0 = unsafe { _mm_mul_ps(self.0, other.0) };
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             self.0 *= other.0;
             self.1 *= other.1;
@@ -1109,12 +1126,12 @@ impl Mul<f32> for Vec4 {
     type Output = Self;
     #[inline]
     fn mul(self, other: f32) -> Self {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         unsafe {
             Self(_mm_mul_ps(self.0, _mm_set1_ps(other)))
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             Self(
                 self.0 * other,
@@ -1129,12 +1146,12 @@ impl Mul<f32> for Vec4 {
 impl MulAssign<f32> for Vec4 {
     #[inline]
     fn mul_assign(&mut self, other: f32) {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         {
             self.0 = unsafe { _mm_mul_ps(self.0, _mm_set1_ps(other)) };
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             self.0 *= other;
             self.1 *= other;
@@ -1148,12 +1165,12 @@ impl Mul<Vec4> for f32 {
     type Output = Vec4;
     #[inline]
     fn mul(self, other: Vec4) -> Vec4 {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         unsafe {
             Vec4(_mm_mul_ps(_mm_set1_ps(self), other.0))
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             Vec4(
                 self * other.0,
@@ -1169,12 +1186,12 @@ impl Add for Vec4 {
     type Output = Self;
     #[inline]
     fn add(self, other: Self) -> Self {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         unsafe {
             Self(_mm_add_ps(self.0, other.0))
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             Self(
                 self.0 + other.0,
@@ -1189,12 +1206,12 @@ impl Add for Vec4 {
 impl AddAssign for Vec4 {
     #[inline]
     fn add_assign(&mut self, other: Self) {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         {
             self.0 = unsafe { _mm_add_ps(self.0, other.0) };
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             self.0 += other.0;
             self.1 += other.1;
@@ -1208,12 +1225,12 @@ impl Sub for Vec4 {
     type Output = Self;
     #[inline]
     fn sub(self, other: Self) -> Self {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         unsafe {
             Self(_mm_sub_ps(self.0, other.0))
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             Self(
                 self.0 - other.0,
@@ -1228,12 +1245,12 @@ impl Sub for Vec4 {
 impl SubAssign for Vec4 {
     #[inline]
     fn sub_assign(&mut self, other: Self) {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         {
             self.0 = unsafe { _mm_sub_ps(self.0, other.0) };
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             self.0 -= other.0;
             self.1 -= other.1;
@@ -1247,12 +1264,12 @@ impl Neg for Vec4 {
     type Output = Self;
     #[inline]
     fn neg(self) -> Self {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         unsafe {
             Self(_mm_sub_ps(_mm_set1_ps(0.0), self.0))
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             Self(-self.0, -self.1, -self.2, -self.3)
         }
@@ -1284,7 +1301,7 @@ impl From<(f32, f32, f32, f32)> for Vec4 {
 impl From<Vec4> for (f32, f32, f32, f32) {
     #[inline]
     fn from(v: Vec4) -> Self {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         {
             let mut out: MaybeUninit<Align16<(f32, f32, f32, f32)>> = MaybeUninit::uninit();
             unsafe {
@@ -1293,7 +1310,7 @@ impl From<Vec4> for (f32, f32, f32, f32) {
             }
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             (v.0, v.1, v.2, v.3)
         }
@@ -1303,12 +1320,12 @@ impl From<Vec4> for (f32, f32, f32, f32) {
 impl From<[f32; 4]> for Vec4 {
     #[inline]
     fn from(a: [f32; 4]) -> Self {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         unsafe {
             Self(_mm_loadu_ps(a.as_ptr()))
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             Self(a[0], a[1], a[2], a[3])
         }
@@ -1318,7 +1335,7 @@ impl From<[f32; 4]> for Vec4 {
 impl From<Vec4> for [f32; 4] {
     #[inline]
     fn from(v: Vec4) -> Self {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         {
             let mut out: MaybeUninit<Align16<[f32; 4]>> = MaybeUninit::uninit();
             unsafe {
@@ -1327,7 +1344,7 @@ impl From<Vec4> for [f32; 4] {
             }
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             [v.0, v.1, v.2, v.3]
         }
diff --git a/src/f32/vec4_mask.rs b/src/f32/vec4_mask.rs
index 1293f1f..aa2865b 100644
--- a/src/f32/vec4_mask.rs
+++ b/src/f32/vec4_mask.rs
@@ -1,29 +1,29 @@
 use crate::Vec4;
 use core::{fmt, ops::*};
 
-#[cfg(all(vec4sse2, target_arch = "x86"))]
+#[cfg(all(vec4_sse2, target_arch = "x86"))]
 use core::arch::x86::*;
-#[cfg(all(vec4sse2, target_arch = "x86_64"))]
+#[cfg(all(vec4_sse2, target_arch = "x86_64"))]
 use core::arch::x86_64::*;
-#[cfg(vec4sse2)]
+#[cfg(vec4_sse2)]
 use core::{cmp::Ordering, hash};
 
 /// A 4-dimensional vector mask.
 ///
 /// This type is typically created by comparison methods on `Vec4`.  It is
 /// essentially a vector of four boolean values.
-#[cfg(vec4sse2)]
+#[cfg(vec4_sse2)]
 #[derive(Clone, Copy)]
 #[repr(C)]
 pub struct Vec4Mask(pub(crate) __m128);
 
-#[cfg(vec4f32)]
+#[cfg(vec4_f32)]
 #[derive(Clone, Copy, Default, PartialEq, Eq, Ord, PartialOrd, Hash)]
-#[cfg_attr(vec4f32_align16, repr(align(16)))]
+#[cfg_attr(vec4_f32_align16, repr(align(16)))]
 #[repr(C)]
 pub struct Vec4Mask(u32, u32, u32, u32);
 
-#[cfg(vec4sse2)]
+#[cfg(vec4_sse2)]
 impl Default for Vec4Mask {
     #[inline]
     fn default() -> Self {
@@ -31,7 +31,7 @@ impl Default for Vec4Mask {
     }
 }
 
-#[cfg(vec4sse2)]
+#[cfg(vec4_sse2)]
 impl PartialEq for Vec4Mask {
     #[inline]
     fn eq(&self, other: &Self) -> bool {
@@ -39,10 +39,10 @@ impl PartialEq for Vec4Mask {
     }
 }
 
-#[cfg(vec4sse2)]
+#[cfg(vec4_sse2)]
 impl Eq for Vec4Mask {}
 
-#[cfg(vec4sse2)]
+#[cfg(vec4_sse2)]
 impl Ord for Vec4Mask {
     #[inline]
     fn cmp(&self, other: &Self) -> Ordering {
@@ -50,7 +50,7 @@ impl Ord for Vec4Mask {
     }
 }
 
-#[cfg(vec4sse2)]
+#[cfg(vec4_sse2)]
 impl PartialOrd for Vec4Mask {
     #[inline]
     fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
@@ -58,7 +58,7 @@ impl PartialOrd for Vec4Mask {
     }
 }
 
-#[cfg(vec4sse2)]
+#[cfg(vec4_sse2)]
 impl hash::Hash for Vec4Mask {
     #[inline]
     fn hash<H: hash::Hasher>(&self, state: &mut H) {
@@ -75,7 +75,7 @@ impl Vec4Mask {
         // be created via this function or by `Vec4` methods.
 
         const MASK: [u32; 2] = [0, 0xff_ff_ff_ff];
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         unsafe {
             Self(_mm_set_ps(
                 f32::from_bits(MASK[w as usize]),
@@ -85,7 +85,7 @@ impl Vec4Mask {
             ))
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             Self(
                 MASK[x as usize],
@@ -107,12 +107,12 @@ impl Vec4Mask {
         // _mm_movemask_ps only checks the most significant bit of the u32 is true, so we replicate
         // that here with the non-SSE2 version.
 
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         unsafe {
             _mm_movemask_ps(self.0) as u32
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             (self.0 & 0x1) | (self.1 & 0x1) << 1 | (self.2 & 0x1) << 2 | (self.3 & 0x1) << 3
         }
@@ -123,12 +123,12 @@ impl Vec4Mask {
     /// In other words: `x || y || z || w`.
     #[inline]
     pub fn any(self) -> bool {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         unsafe {
             _mm_movemask_ps(self.0) != 0
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             ((self.0 | self.1 | self.2 | self.3) & 0x1) != 0
         }
@@ -139,12 +139,12 @@ impl Vec4Mask {
     /// In other words: `x && y && z && w`.
     #[inline]
     pub fn all(self) -> bool {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         unsafe {
             _mm_movemask_ps(self.0) == 0xf
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             ((self.0 & self.1 & self.2 & self.3) & 0x1) != 0
         }
@@ -160,7 +160,7 @@ impl Vec4Mask {
         // We are assuming that the mask values are either 0 or 0xff_ff_ff_ff for the SSE2 and f32
         // to behave the same here.
 
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         unsafe {
             Vec4(_mm_or_ps(
                 _mm_andnot_ps(self.0, if_false.0),
@@ -168,7 +168,7 @@ impl Vec4Mask {
             ))
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             Vec4(
                 if self.0 != 0 { if_true.0 } else { if_false.0 },
@@ -184,12 +184,12 @@ impl BitAnd for Vec4Mask {
     type Output = Self;
     #[inline]
     fn bitand(self, other: Self) -> Self {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         unsafe {
             Self(_mm_and_ps(self.0, other.0))
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             Self(
                 self.0 & other.0,
@@ -204,12 +204,12 @@ impl BitAnd for Vec4Mask {
 impl BitAndAssign for Vec4Mask {
     #[inline]
     fn bitand_assign(&mut self, other: Self) {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         {
             self.0 = unsafe { _mm_and_ps(self.0, other.0) };
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             self.0 &= other.0;
             self.1 &= other.1;
@@ -223,12 +223,12 @@ impl BitOr for Vec4Mask {
     type Output = Self;
     #[inline]
     fn bitor(self, other: Self) -> Self {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         unsafe {
             Self(_mm_or_ps(self.0, other.0))
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             Self(
                 self.0 | other.0,
@@ -243,12 +243,12 @@ impl BitOr for Vec4Mask {
 impl BitOrAssign for Vec4Mask {
     #[inline]
     fn bitor_assign(&mut self, other: Self) {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         {
             self.0 = unsafe { _mm_or_ps(self.0, other.0) };
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             self.0 |= other.0;
             self.1 |= other.1;
@@ -262,7 +262,7 @@ impl Not for Vec4Mask {
     type Output = Self;
     #[inline]
     fn not(self) -> Self {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         unsafe {
             Self(_mm_andnot_ps(
                 self.0,
@@ -270,7 +270,7 @@ impl Not for Vec4Mask {
             ))
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             Self(!self.0, !self.1, !self.2, !self.3)
         }
@@ -279,7 +279,7 @@ impl Not for Vec4Mask {
 
 impl fmt::Debug for Vec4Mask {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        #[cfg(vec4sse2)]
+        #[cfg(vec4_sse2)]
         {
             let arr = self.as_ref();
             write!(
@@ -289,7 +289,7 @@ impl fmt::Debug for Vec4Mask {
             )
         }
 
-        #[cfg(vec4f32)]
+        #[cfg(vec4_f32)]
         {
             write!(
                 f,
diff --git a/src/lib.rs b/src/lib.rs
index bb26051..af686d0 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -124,7 +124,7 @@ and benchmarks.
   parameters passed to `glam` to help catch runtime errors.
 
 */
-#![doc(html_root_url = "https://docs.rs/glam/0.8.7")]
+#![doc(html_root_url = "https://docs.rs/glam/0.9.0-beta0")]
 
 #[macro_use]
 mod macros;
@@ -132,8 +132,8 @@ mod macros;
 pub mod f32;
 
 pub use self::f32::{
-    mat2, mat3, mat4, quat, vec2, vec3, vec4, Mat2, Mat3, Mat4, Quat, Vec2, Vec2Mask, Vec3,
-    Vec3Mask, Vec4, Vec4Mask,
+    mat2, mat3, mat4, quat, vec2, vec3, vec3a, vec4, Mat2, Mat3, Mat4, Quat, Vec2, Vec2Mask, Vec3,
+    Vec3A, Vec3AMask, Vec3Mask, Vec4, Vec4Mask,
 };
 
 #[cfg(feature = "transform-types")]


Test patch:
diff --git a/tests/mat2.rs b/tests/mat2.rs
index b8dcadc..a48f873 100644
--- a/tests/mat2.rs
+++ b/tests/mat2.rs
@@ -1,6 +1,6 @@
 mod support;
 
-use glam::f32::*;
+use glam::{mat2, vec2, Mat2, Vec2};
 use support::deg;
 
 const IDENTITY: [[f32; 2]; 2] = [[1.0, 0.0], [0.0, 1.0]];
diff --git a/tests/mat3.rs b/tests/mat3.rs
index 2999371..6d50d85 100644
--- a/tests/mat3.rs
+++ b/tests/mat3.rs
@@ -1,6 +1,6 @@
 mod support;
 
-use glam::f32::*;
+use glam::{mat3, vec2, vec3, Mat3, Vec2, Vec3};
 use support::deg;
 
 const IDENTITY: [[f32; 3]; 3] = [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]];
@@ -12,13 +12,8 @@ const ZERO: [[f32; 3]; 3] = [[0.0; 3]; 3];
 #[test]
 fn test_mat3_align() {
     use std::mem;
-    if cfg!(any(feature = "packed-vec3", feature = "scalar-math")) {
-        assert_eq!(36, mem::size_of::<Mat3>());
-        assert_eq!(4, mem::align_of::<Mat3>());
-    } else {
-        assert_eq!(48, mem::size_of::<Mat3>());
-        assert_eq!(16, mem::align_of::<Mat3>());
-    }
+    assert_eq!(36, mem::size_of::<Mat3>());
+    assert_eq!(4, mem::align_of::<Mat3>());
 }
 
 #[test]
diff --git a/tests/mat4.rs b/tests/mat4.rs
index 9f2d10c..c792f5e 100644
--- a/tests/mat4.rs
+++ b/tests/mat4.rs
@@ -1,6 +1,6 @@
 mod support;
 
-use glam::f32::*;
+use glam::{mat4, vec3, vec4, Mat4, Quat, Vec3, Vec4};
 use support::deg;
 
 const IDENTITY: [[f32; 4]; 4] = [
@@ -126,7 +126,10 @@ fn test_mat4_mul() {
     let mat_a = Mat4::from_axis_angle(Vec3::unit_z(), deg(90.0));
     let result3 = mat_a.transform_vector3(Vec3::unit_y());
     assert_approx_eq!(vec3(-1.0, 0.0, 0.0), result3);
-    assert_approx_eq!(result3, (mat_a * Vec3::unit_y().extend(0.0)).truncate());
+    assert_approx_eq!(
+        result3,
+        (mat_a * Vec3::unit_y().extend(0.0)).truncate().into()
+    );
     let result4 = mat_a * Vec4::unit_y();
     assert_approx_eq!(vec4(-1.0, 0.0, 0.0, 0.0), result4);
     assert_approx_eq!(result4, mat_a * Vec4::unit_y());
@@ -138,11 +141,17 @@ fn test_mat4_mul() {
     );
     let result3 = mat_b.transform_vector3(Vec3::unit_y());
     assert_approx_eq!(vec3(0.0, 0.0, 1.5), result3, 1.0e-6);
-    assert_approx_eq!(result3, (mat_b * Vec3::unit_y().extend(0.0)).truncate());
+    assert_approx_eq!(
+        result3,
+        (mat_b * Vec3::unit_y().extend(0.0)).truncate().into()
+    );
 
     let result3 = mat_b.transform_point3(Vec3::unit_y());
     assert_approx_eq!(vec3(1.0, 2.0, 4.5), result3, 1.0e-6);
-    assert_approx_eq!(result3, (mat_b * Vec3::unit_y().extend(1.0)).truncate());
+    assert_approx_eq!(
+        result3,
+        (mat_b * Vec3::unit_y().extend(1.0)).truncate().into()
+    );
 }
 
 #[test]
diff --git a/tests/quat.rs b/tests/quat.rs
index def0acc..1ae0a66 100644
--- a/tests/quat.rs
+++ b/tests/quat.rs
@@ -1,6 +1,6 @@
 mod support;
 
-use glam::f32::{quat, Mat3, Mat4, Quat, Vec3, Vec4};
+use glam::{quat, Mat3, Mat4, Quat, Vec3, Vec4};
 use support::{deg, rad};
 
 #[test]
diff --git a/tests/support/mod.rs b/tests/support/mod.rs
index 98bd080..769560b 100644
--- a/tests/support/mod.rs
+++ b/tests/support/mod.rs
@@ -1,7 +1,7 @@
 #[macro_use]
 mod macros;
 
-use glam::{Mat2, Mat3, Mat4, Quat, Vec2, Vec3, Vec4};
+use glam::{Mat2, Mat3, Mat4, Quat, Vec2, Vec3, Vec3A, Vec4};
 
 #[cfg(feature = "transform-types")]
 use glam::{TransformRT, TransformSRT};
@@ -43,11 +43,11 @@ impl FloatCompare for f32 {
 
 impl FloatCompare for Mat2 {
     #[inline]
-    fn approx_eq(&self, other: &Mat2, max_abs_diff: f32) -> bool {
+    fn approx_eq(&self, other: &Self, max_abs_diff: f32) -> bool {
         self.abs_diff_eq(*other, max_abs_diff)
     }
     #[inline]
-    fn abs_diff(&self, other: &Mat2) -> Mat2 {
+    fn abs_diff(&self, other: &Self) -> Self {
         Mat2::from_cols(
             (self.x_axis() - other.x_axis()).abs(),
             (self.y_axis() - other.y_axis()).abs(),
@@ -57,11 +57,11 @@ impl FloatCompare for Mat2 {
 
 impl FloatCompare for Mat3 {
     #[inline]
-    fn approx_eq(&self, other: &Mat3, max_abs_diff: f32) -> bool {
+    fn approx_eq(&self, other: &Self, max_abs_diff: f32) -> bool {
         self.abs_diff_eq(*other, max_abs_diff)
     }
     #[inline]
-    fn abs_diff(&self, other: &Mat3) -> Mat3 {
+    fn abs_diff(&self, other: &Self) -> Self {
         Mat3::from_cols(
             (self.x_axis() - other.x_axis()).abs(),
             (self.y_axis() - other.y_axis()).abs(),
@@ -72,11 +72,11 @@ impl FloatCompare for Mat3 {
 
 impl FloatCompare for Mat4 {
     #[inline]
-    fn approx_eq(&self, other: &Mat4, max_abs_diff: f32) -> bool {
+    fn approx_eq(&self, other: &Self, max_abs_diff: f32) -> bool {
         self.abs_diff_eq(*other, max_abs_diff)
     }
     #[inline]
-    fn abs_diff(&self, other: &Mat4) -> Mat4 {
+    fn abs_diff(&self, other: &Self) -> Self {
         Mat4::from_cols(
             (self.x_axis() - other.x_axis()).abs(),
             (self.y_axis() - other.y_axis()).abs(),
@@ -88,11 +88,11 @@ impl FloatCompare for Mat4 {
 
 impl FloatCompare for Quat {
     #[inline]
-    fn approx_eq(&self, other: &Quat, max_abs_diff: f32) -> bool {
+    fn approx_eq(&self, other: &Self, max_abs_diff: f32) -> bool {
         self.abs_diff_eq(*other, max_abs_diff)
     }
     #[inline]
-    fn abs_diff(&self, other: &Quat) -> Quat {
+    fn abs_diff(&self, other: &Self) -> Self {
         let a: Vec4 = (*self).into();
         let b: Vec4 = (*other).into();
         (a - b).abs().into()
@@ -101,33 +101,44 @@ impl FloatCompare for Quat {
 
 impl FloatCompare for Vec2 {
     #[inline]
-    fn approx_eq(&self, other: &Vec2, max_abs_diff: f32) -> bool {
+    fn approx_eq(&self, other: &Self, max_abs_diff: f32) -> bool {
         self.abs_diff_eq(*other, max_abs_diff)
     }
     #[inline]
-    fn abs_diff(&self, other: &Vec2) -> Vec2 {
+    fn abs_diff(&self, other: &Self) -> Self {
         (*self - *other).abs()
     }
 }
 
 impl FloatCompare for Vec3 {
     #[inline]
-    fn approx_eq(&self, other: &Vec3, max_abs_diff: f32) -> bool {
+    fn approx_eq(&self, other: &Self, max_abs_diff: f32) -> bool {
+        self.abs_diff_eq(*other, max_abs_diff)
+    }
+    #[inline]
+    fn abs_diff(&self, other: &Self) -> Self {
+        (*self - *other).abs()
+    }
+}
+
+impl FloatCompare for Vec3A {
+    #[inline]
+    fn approx_eq(&self, other: &Self, max_abs_diff: f32) -> bool {
         self.abs_diff_eq(*other, max_abs_diff)
     }
     #[inline]
-    fn abs_diff(&self, other: &Vec3) -> Vec3 {
+    fn abs_diff(&self, other: &Self) -> Self {
         (*self - *other).abs()
     }
 }
 
 impl FloatCompare for Vec4 {
     #[inline]
-    fn approx_eq(&self, other: &Vec4, max_abs_diff: f32) -> bool {
+    fn approx_eq(&self, other: &Self, max_abs_diff: f32) -> bool {
         self.abs_diff_eq(*other, max_abs_diff)
     }
     #[inline]
-    fn abs_diff(&self, other: &Vec4) -> Vec4 {
+    fn abs_diff(&self, other: &Self) -> Self {
         (*self - *other).abs()
     }
 }
diff --git a/tests/vec2.rs b/tests/vec2.rs
index 2a16cd9..9c64f0e 100644
--- a/tests/vec2.rs
+++ b/tests/vec2.rs
@@ -1,6 +1,6 @@
 mod support;
 
-use glam::*;
+use glam::{vec2, vec3, Vec2, Vec2Mask, Vec3};
 use std::f32;
 
 #[test]
@@ -114,8 +114,10 @@ fn test_vec2_ops() {
     assert_eq!(vec2(0.0, 0.0), (a - a));
     assert_eq!(vec2(1.0, 4.0), (a * a));
     assert_eq!(vec2(2.0, 4.0), (a * 2.0));
+    assert_eq!(vec2(2.0, 4.0), (2.0 * a));
     assert_eq!(vec2(1.0, 1.0), (a / a));
     assert_eq!(vec2(0.5, 1.0), (a / 2.0));
+    assert_eq!(vec2(2.0, 1.0), (2.0 / a));
     assert_eq!(vec2(-1.0, -2.0), (-a));
 }
 
@@ -201,7 +203,7 @@ fn test_extend_truncate() {
 }
 
 #[test]
-fn test_vec2b() {
+fn test_vec2mask() {
     // make sure the unused 'w' value doesn't break Vec2b behaviour
     let a = Vec3::zero();
     let mut b = a.truncate();
diff --git a/tests/vec3.rs b/tests/vec3.rs
index dd5954d..1842a65 100644
--- a/tests/vec3.rs
+++ b/tests/vec3.rs
@@ -1,22 +1,15 @@
 mod support;
 
-use glam::*;
+use glam::{vec3, Vec3, Vec3Mask};
 use std::f32;
 
 #[test]
 fn test_vec3_align() {
     use std::mem;
-    if cfg!(any(feature = "packed-vec3", feature = "scalar-math")) {
-        assert_eq!(12, mem::size_of::<Vec3>());
-        assert_eq!(4, mem::align_of::<Vec3>());
-        assert_eq!(12, mem::size_of::<Vec3Mask>());
-        assert_eq!(4, mem::align_of::<Vec3Mask>());
-    } else {
-        assert_eq!(16, mem::size_of::<Vec3>());
-        assert_eq!(16, mem::align_of::<Vec3>());
-        assert_eq!(16, mem::size_of::<Vec3Mask>());
-        assert_eq!(16, mem::align_of::<Vec3Mask>());
-    }
+    assert_eq!(12, mem::size_of::<Vec3>());
+    assert_eq!(4, mem::align_of::<Vec3>());
+    assert_eq!(12, mem::size_of::<Vec3Mask>());
+    assert_eq!(4, mem::align_of::<Vec3Mask>());
 }
 
 #[test]
@@ -47,17 +40,6 @@ fn test_vec3_new() {
 #[test]
 fn test_vec3_fmt() {
     let a = Vec3::new(1.0, 2.0, 3.0);
-    #[cfg(all(
-        target_feature = "sse2",
-        not(feature = "packed-vec3"),
-        not(feature = "scalar-math")
-    ))]
-    assert_eq!(format!("{:?}", a), "Vec3(__m128(1.0, 2.0, 3.0, 3.0))");
-    #[cfg(any(
-        not(target_feature = "sse2"),
-        feature = "packed-vec3",
-        feature = "scalar-math"
-    ))]
     assert_eq!(format!("{:?}", a), "Vec3(1.0, 2.0, 3.0)");
     // assert_eq!(format!("{:#?}", a), "Vec3(\n    1.0,\n    2.0,\n    3.0\n)");
     assert_eq!(format!("{}", a), "[1, 2, 3]");
@@ -154,8 +136,10 @@ fn test_vec3_ops() {
     assert_eq!((0.0, 0.0, 0.0), (a - a).into());
     assert_eq!((1.0, 4.0, 9.0), (a * a).into());
     assert_eq!((2.0, 4.0, 6.0), (a * 2.0).into());
+    assert_eq!((2.0, 4.0, 6.0), (2.0 * a).into());
     assert_eq!((1.0, 1.0, 1.0), (a / a).into());
     assert_eq!((0.5, 1.0, 1.5), (a / 2.0).into());
+    assert_eq!((2.0, 1.0, 2.0 / 3.0), (2.0 / a).into());
     assert_eq!((-1.0, -2.0, -3.0), (-a).into());
 }
 
@@ -239,20 +223,18 @@ fn test_extend_truncate() {
     let a = vec3(1.0, 2.0, 3.0);
     let b = a.extend(4.0);
     assert_eq!((1.0, 2.0, 3.0, 4.0), b.into());
-    let c = b.truncate();
+    let c = Vec3::from(b.truncate());
     assert_eq!(a, c);
 }
 
 #[test]
-fn test_vec3b() {
-    // make sure the unused 'w' value doesn't break Vec3b behaviour
-    let a = Vec4::zero();
-    let mut b = a.truncate();
-    b.set_x(1.0);
-    b.set_y(1.0);
-    b.set_z(1.0);
-    assert!(!b.cmpeq(Vec3::zero()).any());
-    assert!(b.cmpeq(Vec3::splat(1.0)).all());
+fn test_vec3_mask() {
+    let mut a = Vec3::zero();
+    a.set_x(1.0);
+    a.set_y(1.0);
+    a.set_z(1.0);
+    assert!(!a.cmpeq(Vec3::zero()).any());
+    assert!(a.cmpeq(Vec3::splat(1.0)).all());
 }
 
 #[test]
@@ -402,33 +384,9 @@ fn test_vec3mask_fmt() {
     let a = Vec3Mask::new(true, false, false);
 
     // debug fmt
-    #[cfg(all(
-        target_feature = "sse2",
-        not(feature = "packed-vec3"),
-        not(feature = "scalar-math")
-    ))]
-    assert_eq!(format!("{:?}", a), "Vec3Mask(0xffffffff, 0x0, 0x0)");
-
-    #[cfg(any(
-        not(target_feature = "sse2"),
-        feature = "packed-vec3",
-        feature = "scalar-math"
-    ))]
     assert_eq!(format!("{:?}", a), "Vec3Mask(0xffffffff, 0x0, 0x0)");
 
     // display fmt
-    #[cfg(all(
-        target_feature = "sse2",
-        not(feature = "packed-vec3"),
-        not(feature = "scalar-math")
-    ))]
-    assert_eq!(format!("{}", a), "[true, false, false]");
-
-    #[cfg(any(
-        not(target_feature = "sse2"),
-        feature = "packed-vec3",
-        feature = "scalar-math"
-    ))]
     assert_eq!(format!("{}", a), "[true, false, false]");
 }
 
diff --git a/tests/vec3a.rs b/tests/vec3a.rs
new file mode 100644
index 0000000..e132b8b
--- /dev/null
+++ b/tests/vec3a.rs
@@ -0,0 +1,572 @@
+mod support;
+
+use glam::{vec3a, Vec3A, Vec3AMask, Vec4};
+use std::f32;
+
+#[test]
+fn test_vec3a_align() {
+    use std::mem;
+    assert_eq!(16, mem::size_of::<Vec3A>());
+    assert_eq!(16, mem::align_of::<Vec3A>());
+    assert_eq!(16, mem::size_of::<Vec3AMask>());
+    assert_eq!(16, mem::align_of::<Vec3AMask>());
+}
+
+#[test]
+fn test_vec3a_new() {
+    let v = vec3a(1.0, 2.0, 3.0);
+
+    assert_eq!(v.x(), 1.0);
+    assert_eq!(v.y(), 2.0);
+    assert_eq!(v.z(), 3.0);
+
+    let t = (1.0, 2.0, 3.0);
+    let v = Vec3A::from(t);
+    assert_eq!(t, v.into());
+
+    let a = [1.0, 2.0, 3.0];
+    let v = Vec3A::from(a);
+    let a1: [f32; 3] = v.into();
+    assert_eq!(a, a1);
+
+    let v = Vec3A::new(t.0, t.1, t.2);
+    assert_eq!(t, v.into());
+
+    assert_eq!(Vec3A::new(1.0, 0.0, 0.0), Vec3A::unit_x());
+    assert_eq!(Vec3A::new(0.0, 1.0, 0.0), Vec3A::unit_y());
+    assert_eq!(Vec3A::new(0.0, 0.0, 1.0), Vec3A::unit_z());
+}
+
+#[test]
+fn test_vec3a_fmt() {
+    let a = Vec3A::new(1.0, 2.0, 3.0);
+    #[cfg(vec3a_sse2)]
+    assert_eq!(format!("{:?}", a), "Vec3A(__m128(1.0, 2.0, 3.0, 3.0))");
+    #[cfg(vec3a_f32)]
+    assert_eq!(format!("{:?}", a), "Vec3A(Vec3(1.0, 2.0, 3.0))");
+    // assert_eq!(format!("{:#?}", a), "Vec3A(\n    1.0,\n    2.0,\n    3.0\n)");
+    assert_eq!(format!("{}", a), "[1, 2, 3]");
+}
+
+#[test]
+fn test_vec3a_zero() {
+    let v = Vec3A::zero();
+    assert_eq!((0.0, 0.0, 0.0), v.into());
+    assert_eq!(v, Vec3A::default());
+}
+
+#[test]
+fn test_vec3a_splat() {
+    let v = Vec3A::splat(1.0);
+    assert_eq!((1.0, 1.0, 1.0), v.into());
+}
+
+#[test]
+fn test_vec3a_accessors() {
+    let mut a = Vec3A::zero();
+    a.set_x(1.0);
+    a.set_y(2.0);
+    a.set_z(3.0);
+    assert_eq!(1.0, a.x());
+    assert_eq!(2.0, a.y());
+    assert_eq!(3.0, a.z());
+    assert_eq!((1.0, 2.0, 3.0), a.into());
+
+    let mut a = Vec3A::zero();
+    *a.x_mut() = 1.0;
+    *a.y_mut() = 2.0;
+    *a.z_mut() = 3.0;
+    assert_eq!(1.0, a.x());
+    assert_eq!(2.0, a.y());
+    assert_eq!(3.0, a.z());
+    assert_eq!((1.0, 2.0, 3.0), a.into());
+
+    let mut a = Vec3A::zero();
+    a[0] = 1.0;
+    a[1] = 2.0;
+    a[2] = 3.0;
+    assert_eq!(1.0, a[0]);
+    assert_eq!(2.0, a[1]);
+    assert_eq!(3.0, a[2]);
+    assert_eq!((1.0, 2.0, 3.0), a.into());
+}
+
+#[test]
+fn test_vec3a_funcs() {
+    let x = vec3a(1.0, 0.0, 0.0);
+    let y = vec3a(0.0, 1.0, 0.0);
+    let z = vec3a(0.0, 0.0, 1.0);
+    assert_eq!(1.0, x.dot(x));
+    assert_eq!(0.0, x.dot(y));
+    assert_eq!(-1.0, z.dot(-z));
+    assert_eq!(y, z.cross(x));
+    assert_eq!(z, x.cross(y));
+    assert_eq!(4.0, (2.0 * x).length_squared());
+    assert_eq!(9.0, (-3.0 * y).length_squared());
+    assert_eq!(16.0, (4.0 * z).length_squared());
+    assert_eq!(2.0, (-2.0 * x).length());
+    assert_eq!(3.0, (3.0 * y).length());
+    assert_eq!(4.0, (-4.0 * z).length());
+    assert_eq!(x, (2.0 * x).normalize());
+    assert_eq!(
+        1.0 * 4.0 + 2.0 * 5.0 + 3.0 * 6.0,
+        vec3a(1.0, 2.0, 3.0).dot(vec3a(4.0, 5.0, 6.0))
+    );
+    assert_eq!(
+        2.0 * 2.0 + 3.0 * 3.0 + 4.0 * 4.0,
+        vec3a(2.0, 3.0, 4.0).length_squared()
+    );
+    assert_eq!(
+        (2.0_f32 * 2.0 + 3.0 * 3.0 + 4.0 * 4.0).sqrt(),
+        vec3a(2.0, 3.0, 4.0).length()
+    );
+    assert_eq!(
+        1.0 / (2.0_f32 * 2.0 + 3.0 * 3.0 + 4.0 * 4.0).sqrt(),
+        vec3a(2.0, 3.0, 4.0).length_reciprocal()
+    );
+    assert!(vec3a(2.0, 3.0, 4.0).normalize().is_normalized());
+    assert_approx_eq!(
+        vec3a(2.0, 3.0, 4.0) / (2.0_f32 * 2.0 + 3.0 * 3.0 + 4.0 * 4.0).sqrt(),
+        vec3a(2.0, 3.0, 4.0).normalize()
+    );
+    assert_eq!(vec3a(0.5, 0.25, 0.125), vec3a(2.0, 4.0, 8.0).reciprocal());
+}
+
+#[test]
+fn test_vec3a_ops() {
+    let a = vec3a(1.0, 2.0, 3.0);
+    assert_eq!((2.0, 4.0, 6.0), (a + a).into());
+    assert_eq!((0.0, 0.0, 0.0), (a - a).into());
+    assert_eq!((1.0, 4.0, 9.0), (a * a).into());
+    assert_eq!((2.0, 4.0, 6.0), (a * 2.0).into());
+    assert_eq!((2.0, 4.0, 6.0), (2.0 * a).into());
+    assert_eq!((1.0, 1.0, 1.0), (a / a).into());
+    assert_eq!((0.5, 1.0, 1.5), (a / 2.0).into());
+    assert_eq!((2.0, 1.0, 2.0 / 3.0), (2.0 / a).into());
+    assert_eq!((-1.0, -2.0, -3.0), (-a).into());
+}
+
+#[test]
+fn test_vec3a_assign_ops() {
+    let a = vec3a(1.0, 2.0, 3.0);
+    let mut b = a;
+    b += a;
+    assert_eq!((2.0, 4.0, 6.0), b.into());
+    b -= a;
+    assert_eq!((1.0, 2.0, 3.0), b.into());
+    b *= a;
+    assert_eq!((1.0, 4.0, 9.0), b.into());
+    b /= a;
+    assert_eq!((1.0, 2.0, 3.0), b.into());
+    b *= 2.0;
+    assert_eq!((2.0, 4.0, 6.0), b.into());
+    b /= 2.0;
+    assert_eq!((1.0, 2.0, 3.0), b.into());
+}
+
+#[test]
+fn test_vec3a_min_max() {
+    let a = vec3a(-1.0, 2.0, -3.0);
+    let b = vec3a(1.0, -2.0, 3.0);
+    assert_eq!((-1.0, -2.0, -3.0), a.min(b).into());
+    assert_eq!((-1.0, -2.0, -3.0), b.min(a).into());
+    assert_eq!((1.0, 2.0, 3.0), a.max(b).into());
+    assert_eq!((1.0, 2.0, 3.0), b.max(a).into());
+}
+
+#[test]
+fn test_vec3a_hmin_hmax() {
+    let a = vec3a(-1.0, 2.0, -3.0);
+    assert_eq!(-3.0, a.min_element());
+    assert_eq!(2.0, a.max_element());
+}
+
+#[test]
+fn test_vec3a_eq() {
+    let a = vec3a(1.0, 1.0, 1.0);
+    let b = vec3a(1.0, 2.0, 3.0);
+    assert!(a.cmpeq(a).all());
+    assert!(b.cmpeq(b).all());
+    assert!(a.cmpne(b).any());
+    assert!(b.cmpne(a).any());
+    assert!(b.cmpeq(a).any());
+}
+
+#[test]
+fn test_vec3a_cmp() {
+    assert!(!Vec3AMask::default().any());
+    assert!(!Vec3AMask::default().all());
+    assert_eq!(Vec3AMask::default().bitmask(), 0x0);
+    let a = vec3a(-1.0, -1.0, -1.0);
+    let b = vec3a(1.0, 1.0, 1.0);
+    let c = vec3a(-1.0, -1.0, 1.0);
+    let d = vec3a(1.0, -1.0, -1.0);
+    assert_eq!(a.cmplt(a).bitmask(), 0x0);
+    assert_eq!(a.cmplt(b).bitmask(), 0x7);
+    assert_eq!(a.cmplt(c).bitmask(), 0x4);
+    assert_eq!(c.cmple(a).bitmask(), 0x3);
+    assert_eq!(a.cmplt(d).bitmask(), 0x1);
+    assert!(a.cmplt(b).all());
+    assert!(a.cmplt(c).any());
+    assert!(a.cmple(b).all());
+    assert!(a.cmple(a).all());
+    assert!(b.cmpgt(a).all());
+    assert!(b.cmpge(a).all());
+    assert!(b.cmpge(b).all());
+    assert!(!(a.cmpge(c).all()));
+    assert!(c.cmple(c).all());
+    assert!(c.cmpge(c).all());
+    assert!(a == a);
+    assert!(a < b);
+    assert!(b > a);
+}
+
+#[test]
+fn test_extend_truncate() {
+    let a = vec3a(1.0, 2.0, 3.0);
+    let b = a.extend(4.0);
+    assert_eq!((1.0, 2.0, 3.0, 4.0), b.into());
+    let c = b.truncate();
+    assert_eq!(a, c);
+}
+
+#[test]
+fn test_vec3_mask_align16() {
+    // make sure the unused 'w' value doesn't break Vec3Ab behaviour
+    let a = Vec4::zero();
+    let mut b = a.truncate();
+    b.set_x(1.0);
+    b.set_y(1.0);
+    b.set_z(1.0);
+    assert!(!b.cmpeq(Vec3A::zero()).any());
+    assert!(b.cmpeq(Vec3A::splat(1.0)).all());
+}
+
+#[test]
+fn test_vec3mask_align16_as_ref() {
+    assert_eq!(Vec3AMask::new(false, false, false).as_ref(), &[0, 0, 0]);
+    assert_eq!(Vec3AMask::new(true, false, false).as_ref(), &[!0, 0, 0]);
+    assert_eq!(Vec3AMask::new(false, true, true).as_ref(), &[0, !0, !0]);
+    assert_eq!(Vec3AMask::new(false, true, false).as_ref(), &[0, !0, 0]);
+    assert_eq!(Vec3AMask::new(true, false, true).as_ref(), &[!0, 0, !0]);
+    assert_eq!(Vec3AMask::new(true, true, true).as_ref(), &[!0, !0, !0]);
+}
+
+#[test]
+fn test_vec3mask_from() {
+    assert_eq!(
+        Into::<[u32; 3]>::into(Vec3AMask::new(false, false, false)),
+        [0, 0, 0]
+    );
+    assert_eq!(
+        Into::<[u32; 3]>::into(Vec3AMask::new(true, false, false)),
+        [!0, 0, 0]
+    );
+    assert_eq!(
+        Into::<[u32; 3]>::into(Vec3AMask::new(false, true, true)),
+        [0, !0, !0]
+    );
+    assert_eq!(
+        Into::<[u32; 3]>::into(Vec3AMask::new(false, true, false)),
+        [0, !0, 0]
+    );
+    assert_eq!(
+        Into::<[u32; 3]>::into(Vec3AMask::new(true, false, true)),
+        [!0, 0, !0]
+    );
+    assert_eq!(
+        Into::<[u32; 3]>::into(Vec3AMask::new(true, true, true)),
+        [!0, !0, !0]
+    );
+}
+
+#[test]
+fn test_vec3mask_bitmask() {
+    assert_eq!(Vec3AMask::new(false, false, false).bitmask(), 0b000);
+    assert_eq!(Vec3AMask::new(true, false, false).bitmask(), 0b001);
+    assert_eq!(Vec3AMask::new(false, true, true).bitmask(), 0b110);
+    assert_eq!(Vec3AMask::new(false, true, false).bitmask(), 0b010);
+    assert_eq!(Vec3AMask::new(true, false, true).bitmask(), 0b101);
+    assert_eq!(Vec3AMask::new(true, true, true).bitmask(), 0b111);
+}
+
+#[test]
+fn test_vec3mask_any() {
+    assert_eq!(Vec3AMask::new(false, false, false).any(), false);
+    assert_eq!(Vec3AMask::new(true, false, false).any(), true);
+    assert_eq!(Vec3AMask::new(false, true, false).any(), true);
+    assert_eq!(Vec3AMask::new(false, false, true).any(), true);
+}
+
+#[test]
+fn test_vec3mask_all() {
+    assert_eq!(Vec3AMask::new(true, true, true).all(), true);
+    assert_eq!(Vec3AMask::new(false, true, true).all(), false);
+    assert_eq!(Vec3AMask::new(true, false, true).all(), false);
+    assert_eq!(Vec3AMask::new(true, true, false).all(), false);
+}
+
+#[test]
+fn test_vec3mask_select() {
+    let a = Vec3A::new(1.0, 2.0, 3.0);
+    let b = Vec3A::new(4.0, 5.0, 6.0);
+    assert_eq!(
+        Vec3AMask::new(true, true, true).select(a, b),
+        Vec3A::new(1.0, 2.0, 3.0),
+    );
+    assert_eq!(
+        Vec3AMask::new(true, false, true).select(a, b),
+        Vec3A::new(1.0, 5.0, 3.0),
+    );
+    assert_eq!(
+        Vec3AMask::new(false, true, false).select(a, b),
+        Vec3A::new(4.0, 2.0, 6.0),
+    );
+    assert_eq!(
+        Vec3AMask::new(false, false, false).select(a, b),
+        Vec3A::new(4.0, 5.0, 6.0),
+    );
+}
+
+#[test]
+fn test_vec3mask_and() {
+    assert_eq!(
+        (Vec3AMask::new(false, false, false) & Vec3AMask::new(false, false, false)).bitmask(),
+        0b000,
+    );
+    assert_eq!(
+        (Vec3AMask::new(true, true, true) & Vec3AMask::new(true, true, true)).bitmask(),
+        0b111,
+    );
+    assert_eq!(
+        (Vec3AMask::new(true, false, true) & Vec3AMask::new(false, true, false)).bitmask(),
+        0b000,
+    );
+    assert_eq!(
+        (Vec3AMask::new(true, false, true) & Vec3AMask::new(true, true, true)).bitmask(),
+        0b101,
+    );
+
+    let mut mask = Vec3AMask::new(true, true, false);
+    mask &= Vec3AMask::new(true, false, false);
+    assert_eq!(mask.bitmask(), 0b001);
+}
+
+#[test]
+fn test_vec3mask_or() {
+    assert_eq!(
+        (Vec3AMask::new(false, false, false) | Vec3AMask::new(false, false, false)).bitmask(),
+        0b000,
+    );
+    assert_eq!(
+        (Vec3AMask::new(true, true, true) | Vec3AMask::new(true, true, true)).bitmask(),
+        0b111,
+    );
+    assert_eq!(
+        (Vec3AMask::new(true, false, true) | Vec3AMask::new(false, true, false)).bitmask(),
+        0b111,
+    );
+    assert_eq!(
+        (Vec3AMask::new(true, false, true) | Vec3AMask::new(true, false, true)).bitmask(),
+        0b101,
+    );
+
+    let mut mask = Vec3AMask::new(true, true, false);
+    mask |= Vec3AMask::new(true, false, false);
+    assert_eq!(mask.bitmask(), 0b011);
+}
+
+#[test]
+fn test_vec3mask_not() {
+    assert_eq!((!Vec3AMask::new(false, false, false)).bitmask(), 0b111);
+    assert_eq!((!Vec3AMask::new(true, true, true)).bitmask(), 0b000);
+    assert_eq!((!Vec3AMask::new(true, false, true)).bitmask(), 0b010);
+    assert_eq!((!Vec3AMask::new(false, true, false)).bitmask(), 0b101);
+}
+
+#[test]
+fn test_vec3mask_fmt() {
+    let a = Vec3AMask::new(true, false, false);
+
+    // debug fmt
+    #[cfg(all(target_feature = "sse2", not(feature = "scalar-math")))]
+    assert_eq!(format!("{:?}", a), "Vec3AMask(0xffffffff, 0x0, 0x0)");
+
+    #[cfg(any(not(target_feature = "sse2"), feature = "scalar-math"))]
+    assert_eq!(format!("{:?}", a), "Vec3AMask(0xffffffff, 0x0, 0x0)");
+
+    // display fmt
+    #[cfg(all(target_feature = "sse2", not(feature = "scalar-math")))]
+    assert_eq!(format!("{}", a), "[true, false, false]");
+
+    #[cfg(any(not(target_feature = "sse2"), feature = "scalar-math"))]
+    assert_eq!(format!("{}", a), "[true, false, false]");
+}
+
+#[test]
+fn test_vec3mask_eq() {
+    let a = Vec3AMask::new(true, false, true);
+    let b = Vec3AMask::new(true, false, true);
+    let c = Vec3AMask::new(false, true, true);
+
+    assert_eq!(a, b);
+    assert_eq!(b, a);
+    assert_ne!(a, c);
+    assert_ne!(b, c);
+
+    assert!(a > c);
+    assert!(c < a);
+}
+
+#[test]
+fn test_vec3mask_hash() {
+    use std::collections::hash_map::DefaultHasher;
+    use std::hash::Hash;
+    use std::hash::Hasher;
+
+    let a = Vec3AMask::new(true, false, true);
+    let b = Vec3AMask::new(true, false, true);
+    let c = Vec3AMask::new(false, true, true);
+
+    let mut hasher = DefaultHasher::new();
+    a.hash(&mut hasher);
+    let a_hashed = hasher.finish();
+
+    let mut hasher = DefaultHasher::new();
+    b.hash(&mut hasher);
+    let b_hashed = hasher.finish();
+
+    let mut hasher = DefaultHasher::new();
+    c.hash(&mut hasher);
+    let c_hashed = hasher.finish();
+
+    assert_eq!(a, b);
+    assert_eq!(a_hashed, b_hashed);
+    assert_ne!(a, c);
+    assert_ne!(a_hashed, c_hashed);
+}
+
+#[test]
+fn test_vec3a_sign() {
+    assert_eq!(Vec3A::zero().sign(), Vec3A::one());
+    assert_eq!(-Vec3A::zero().sign(), -Vec3A::one());
+    assert_eq!(Vec3A::one().sign(), Vec3A::one());
+    assert_eq!((-Vec3A::one()).sign(), -Vec3A::one());
+    assert_eq!(Vec3A::splat(core::f32::NEG_INFINITY).sign(), -Vec3A::one());
+}
+
+#[test]
+fn test_vec3a_abs() {
+    assert_eq!(Vec3A::zero().abs(), Vec3A::zero());
+    assert_eq!(Vec3A::one().abs(), Vec3A::one());
+    assert_eq!((-Vec3A::one()).abs(), Vec3A::one());
+}
+
+#[test]
+fn test_vec3a_round() {
+    assert_eq!(Vec3A::new(1.35, 0.0, 0.0).round().x(), 1.0);
+    assert_eq!(Vec3A::new(0.0, 1.5, 0.0).round().y(), 2.0);
+    assert_eq!(Vec3A::new(0.0, 0.0, -15.5).round().z(), -16.0);
+    assert_eq!(Vec3A::new(0.0, 0.0, 0.0).round().z(), 0.0);
+    assert_eq!(Vec3A::new(0.0, 21.1, 0.0).round().y(), 21.0);
+    assert_eq!(Vec3A::new(0.0, 11.123, 0.0).round().y(), 11.0);
+    assert_eq!(Vec3A::new(0.0, 11.499, 0.0).round().y(), 11.0);
+    assert_eq!(
+        Vec3A::new(f32::NEG_INFINITY, f32::INFINITY, 0.0).round(),
+        Vec3A::new(f32::NEG_INFINITY, f32::INFINITY, 0.0)
+    );
+    assert!(Vec3A::new(f32::NAN, 0.0, 0.0).round().x().is_nan());
+}
+
+#[test]
+fn test_vec3a_floor() {
+    assert_eq!(
+        Vec3A::new(1.35, 1.5, -1.5).floor(),
+        Vec3A::new(1.0, 1.0, -2.0)
+    );
+    assert_eq!(
+        Vec3A::new(f32::INFINITY, f32::NEG_INFINITY, 0.0).floor(),
+        Vec3A::new(f32::INFINITY, f32::NEG_INFINITY, 0.0)
+    );
+    assert!(Vec3A::new(f32::NAN, 0.0, 0.0).floor().x().is_nan());
+    assert_eq!(
+        Vec3A::new(-2000000.123, 10000000.123, 1000.9).floor(),
+        Vec3A::new(-2000001.0, 10000000.0, 1000.0)
+    );
+}
+
+#[test]
+fn test_vec3a_ceil() {
+    assert_eq!(
+        Vec3A::new(1.35, 1.5, -1.5).ceil(),
+        Vec3A::new(2.0, 2.0, -1.0)
+    );
+    assert_eq!(
+        Vec3A::new(f32::INFINITY, f32::NEG_INFINITY, 0.0).ceil(),
+        Vec3A::new(f32::INFINITY, f32::NEG_INFINITY, 0.0)
+    );
+    assert!(Vec3A::new(f32::NAN, 0.0, 0.0).ceil().x().is_nan());
+    assert_eq!(
+        Vec3A::new(-2000000.123, 1000000.123, 1000.9).ceil(),
+        Vec3A::new(-2000000.0, 1000001.0, 1001.0)
+    );
+}
+
+#[test]
+fn test_vec3a_lerp() {
+    let v0 = Vec3A::new(-1.0, -1.0, -1.0);
+    let v1 = Vec3A::new(1.0, 1.0, 1.0);
+    assert_approx_eq!(v0, v0.lerp(v1, 0.0));
+    assert_approx_eq!(v1, v0.lerp(v1, 1.0));
+    assert_approx_eq!(Vec3A::zero(), v0.lerp(v1, 0.5));
+}
+
+#[test]
+fn test_vec3a_to_from_slice() {
+    let v = Vec3A::new(1.0, 2.0, 3.0);
+    let mut a = [0.0, 0.0, 0.0];
+    v.write_to_slice_unaligned(&mut a);
+    assert_eq!(v, Vec3A::from_slice_unaligned(&a));
+}
+
+#[test]
+fn test_vec3a_angle_between() {
+    let angle = Vec3A::new(1.0, 0.0, 1.0).angle_between(Vec3A::new(1.0, 1.0, 0.0));
+    assert_approx_eq!(f32::consts::FRAC_PI_3, angle, 1e-6);
+
+    let angle = Vec3A::new(10.0, 0.0, 10.0).angle_between(Vec3A::new(5.0, 5.0, 0.0));
+    assert_approx_eq!(f32::consts::FRAC_PI_3, angle, 1e-6);
+
+    let angle = Vec3A::new(-1.0, 0.0, -1.0).angle_between(Vec3A::new(1.0, -1.0, 0.0));
+    assert_approx_eq!(2.0 * f32::consts::FRAC_PI_3, angle, 1e-6);
+}
+
+#[cfg(feature = "serde")]
+#[test]
+fn test_vec3a_serde() {
+    let a = Vec3A::new(1.0, 2.0, 3.0);
+    let serialized = serde_json::to_string(&a).unwrap();
+    assert_eq!(serialized, "[1.0,2.0,3.0]");
+    let deserialized = serde_json::from_str(&serialized).unwrap();
+    assert_eq!(a, deserialized);
+    let deserialized = serde_json::from_str::<Vec3A>("[]");
+    assert!(deserialized.is_err());
+    let deserialized = serde_json::from_str::<Vec3A>("[1.0]");
+    assert!(deserialized.is_err());
+    let deserialized = serde_json::from_str::<Vec3A>("[1.0,2.0]");
+    assert!(deserialized.is_err());
+    let deserialized = serde_json::from_str::<Vec3A>("[1.0,2.0,3.0,4.0]");
+    assert!(deserialized.is_err());
+}
+
+#[cfg(feature = "rand")]
+#[test]
+fn test_vec3a_rand() {
+    use rand::{Rng, SeedableRng};
+    use rand_xoshiro::Xoshiro256Plus;
+    let mut rng1 = Xoshiro256Plus::seed_from_u64(0);
+    let a: (f32, f32, f32) = rng1.gen();
+    let mut rng2 = Xoshiro256Plus::seed_from_u64(0);
+    let b: Vec3A = rng2.gen();
+    assert_eq!(a, b.into());
+}
diff --git a/tests/vec4.rs b/tests/vec4.rs
index eb206cc..b7c8578 100644
--- a/tests/vec4.rs
+++ b/tests/vec4.rs
@@ -1,6 +1,6 @@
 mod support;
 
-use glam::*;
+use glam::{vec4, Vec4, Vec4Mask};
 use std::f32;
 
 #[test]
@@ -162,8 +162,7 @@ fn test_vec4_ops() {
     assert_eq!((2.0, 4.0, 6.0, 8.0), (2.0 * a).into());
     assert_eq!((1.0, 1.0, 1.0, 1.0), (a / a).into());
     assert_eq!((0.5, 1.0, 1.5, 2.0), (a / 2.0).into());
-    // is this a sensible operator?
-    // assert_eq!((1.0, 0.5, 1.0/3.0, 0.25), (1.0 / a).into());
+    assert_eq!((1.0, 0.5, 1.0 / 3.0, 0.25), (1.0 / a).into());
     assert_eq!((-1.0, -2.0, -3.0, -4.0), (-a).into());
 }