Repository: bitshifter/glam-rs
Pull request #344 (2022-09-29 02:07:34)

Fix negation and signum to consistently handle negative zero across platforms
The implementation gave different results on different target platforms when working with negative zero.

bitshifter: Just needs a rustfmt I think

Patch:
diff --git a/Cargo.toml b/Cargo.toml
index 1240ad5..eaf4bc7 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -50,7 +50,7 @@ bytemuck = { version = "1.5", optional = true, default-features = false }
 mint = { version = "0.5.8", optional = true, default-features = false }
 num-traits = { version = "0.2.14", optional = true, default-features = false }
 rand = { version = "0.8", optional = true, default-features = false }
-serde = { version = "1.0", optional = true, features = ["derive"] }
+serde = { version = "1.0", optional = true, default-features = false, features = ["derive"] }
 rkyv = { version = "0.7", optional = true }
 bytecheck = { version = "0.6", optional = true, default-features = false}
 
diff --git a/codegen/templates/vec.rs.tera b/codegen/templates/vec.rs.tera
index a8ca85b..18506be 100644
--- a/codegen/templates/vec.rs.tera
+++ b/codegen/templates/vec.rs.tera
@@ -430,6 +430,20 @@ impl {{ self_t }} {
         {% endif %}
     }
 
+    /// Returns a vector where every component is the dot product of `self` and `rhs` 
+    #[inline]
+    pub fn dot_into_vec(self, rhs: Self) -> Self {
+        {% if is_sse2 %}
+            Self(unsafe { dot{{ dim }}_into_m128(self.0, rhs.0) })
+        {% elif is_wasm32 %}
+            Self(unsafe { dot{{ dim }}_into_v128(self.0, rhs.0) })
+        {% elif is_coresimd %}
+            Self(unsafe { dot{{ dim }}_into_f32x4(self.0, rhs.0) })
+        {% else %}
+            Self::splat(self.dot(rhs))
+        {% endif %}
+    }
+
 {% if dim == 3 %}
     /// Computes the cross product of `self` and `rhs`.
     #[inline]
@@ -797,11 +811,18 @@ impl {{ self_t }} {
             }
         {% elif is_coresimd %}
             Self(self.0.signum())
-        {% else %}
-            let mask = self.cmpge(Self::ZERO);
-            let result = Self::select(mask, Self::ONE, Self::NEG_ONE);
-            let mask = self.is_nan_mask();
-            Self::select(mask, self, result)
+        {% elif is_sse2 %}
+            unsafe {
+                let result = Self(_mm_or_ps(_mm_and_ps(self.0, Self::NEG_ONE.0), Self::ONE.0));
+                let mask = self.is_nan_mask();
+                Self::select(mask, self, result)
+            }
+        {% elif is_wasm32 %}
+            unsafe {
+                let result = Self(v128_or(v128_and(self.0, Self::NEG_ONE.0), Self::ONE.0));
+                let mask = self.is_nan_mask();
+                Self::select(mask, self, result)
+            }
         {% endif %}
     }
 {% endif %}
@@ -2021,7 +2042,7 @@ impl Neg for {{ self_t }} {
                 {%- endfor %}
             }
         {% elif is_sse2 %}
-            Self(unsafe { _mm_sub_ps(Self::ZERO.0, self.0) })
+            Self(unsafe { _mm_xor_ps(_mm_set1_ps(-0.0), self.0) })
         {% elif is_wasm32 %}
             Self(f32x4_neg(self.0))
         {% elif is_coresimd %}
diff --git a/codegen/templates/vec_mask.rs.tera b/codegen/templates/vec_mask.rs.tera
index cca5010..a604ea6 100644
--- a/codegen/templates/vec_mask.rs.tera
+++ b/codegen/templates/vec_mask.rs.tera
@@ -125,7 +125,17 @@ impl {{ self_t }} {
         {% endif %}
     }
 
-    /// Returns a bitmask with the lowest two bits set from the elements of `self`.
+    /// Creates a vector with all elements set to `v`.
+    #[inline]
+    pub const fn splat(v: bool) -> Self {
+        Self::new(
+            {% for c in components %}
+                v,
+            {%- endfor %}
+        )
+    }
+
+    /// Returns a bitmask with the lowest {{ dim }} bits set from the elements of `self`.
     ///
     /// 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.
diff --git a/src/bool/bvec2.rs b/src/bool/bvec2.rs
index 073dd12..d5f2405 100644
--- a/src/bool/bvec2.rs
+++ b/src/bool/bvec2.rs
@@ -23,7 +23,13 @@ impl BVec2 {
         Self { x, y }
     }
 
-    /// Returns a bitmask with the lowest two bits set from the elements of `self`.
+    /// Creates a vector with all elements set to `v`.
+    #[inline]
+    pub const fn splat(v: bool) -> Self {
+        Self::new(v, v)
+    }
+
+    /// Returns a bitmask with the lowest 2 bits set from the elements of `self`.
     ///
     /// 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.
diff --git a/src/bool/bvec3.rs b/src/bool/bvec3.rs
index b97ed58..171fbf9 100644
--- a/src/bool/bvec3.rs
+++ b/src/bool/bvec3.rs
@@ -24,7 +24,13 @@ impl BVec3 {
         Self { x, y, z }
     }
 
-    /// Returns a bitmask with the lowest two bits set from the elements of `self`.
+    /// Creates a vector with all elements set to `v`.
+    #[inline]
+    pub const fn splat(v: bool) -> Self {
+        Self::new(v, v, v)
+    }
+
+    /// Returns a bitmask with the lowest 3 bits set from the elements of `self`.
     ///
     /// 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.
diff --git a/src/bool/bvec4.rs b/src/bool/bvec4.rs
index f4d366b..5cedba1 100644
--- a/src/bool/bvec4.rs
+++ b/src/bool/bvec4.rs
@@ -25,7 +25,13 @@ impl BVec4 {
         Self { x, y, z, w }
     }
 
-    /// Returns a bitmask with the lowest two bits set from the elements of `self`.
+    /// Creates a vector with all elements set to `v`.
+    #[inline]
+    pub const fn splat(v: bool) -> Self {
+        Self::new(v, v, v, v)
+    }
+
+    /// Returns a bitmask with the lowest 4 bits set from the elements of `self`.
     ///
     /// 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.
diff --git a/src/bool/coresimd/bvec3a.rs b/src/bool/coresimd/bvec3a.rs
index ca592c5..474172b 100644
--- a/src/bool/coresimd/bvec3a.rs
+++ b/src/bool/coresimd/bvec3a.rs
@@ -35,7 +35,13 @@ impl BVec3A {
         }
     }
 
-    /// Returns a bitmask with the lowest two bits set from the elements of `self`.
+    /// Creates a vector with all elements set to `v`.
+    #[inline]
+    pub const fn splat(v: bool) -> Self {
+        Self::new(v, v, v)
+    }
+
+    /// Returns a bitmask with the lowest 3 bits set from the elements of `self`.
     ///
     /// 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.
diff --git a/src/bool/coresimd/bvec4a.rs b/src/bool/coresimd/bvec4a.rs
index 0e70217..3bc0ee7 100644
--- a/src/bool/coresimd/bvec4a.rs
+++ b/src/bool/coresimd/bvec4a.rs
@@ -40,7 +40,13 @@ impl BVec4A {
         }
     }
 
-    /// Returns a bitmask with the lowest two bits set from the elements of `self`.
+    /// Creates a vector with all elements set to `v`.
+    #[inline]
+    pub const fn splat(v: bool) -> Self {
+        Self::new(v, v, v, v)
+    }
+
+    /// Returns a bitmask with the lowest 4 bits set from the elements of `self`.
     ///
     /// 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.
diff --git a/src/bool/scalar/bvec3a.rs b/src/bool/scalar/bvec3a.rs
index 307e4c1..b619c06 100644
--- a/src/bool/scalar/bvec3a.rs
+++ b/src/bool/scalar/bvec3a.rs
@@ -28,7 +28,13 @@ impl BVec3A {
         }
     }
 
-    /// Returns a bitmask with the lowest two bits set from the elements of `self`.
+    /// Creates a vector with all elements set to `v`.
+    #[inline]
+    pub const fn splat(v: bool) -> Self {
+        Self::new(v, v, v)
+    }
+
+    /// Returns a bitmask with the lowest 3 bits set from the elements of `self`.
     ///
     /// 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.
diff --git a/src/bool/scalar/bvec4a.rs b/src/bool/scalar/bvec4a.rs
index 0e133d1..cea5221 100644
--- a/src/bool/scalar/bvec4a.rs
+++ b/src/bool/scalar/bvec4a.rs
@@ -30,7 +30,13 @@ impl BVec4A {
         }
     }
 
-    /// Returns a bitmask with the lowest two bits set from the elements of `self`.
+    /// Creates a vector with all elements set to `v`.
+    #[inline]
+    pub const fn splat(v: bool) -> Self {
+        Self::new(v, v, v, v)
+    }
+
+    /// Returns a bitmask with the lowest 4 bits set from the elements of `self`.
     ///
     /// 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.
diff --git a/src/bool/sse2/bvec3a.rs b/src/bool/sse2/bvec3a.rs
index ed061e7..955d6a0 100644
--- a/src/bool/sse2/bvec3a.rs
+++ b/src/bool/sse2/bvec3a.rs
@@ -38,7 +38,13 @@ impl BVec3A {
         }
     }
 
-    /// Returns a bitmask with the lowest two bits set from the elements of `self`.
+    /// Creates a vector with all elements set to `v`.
+    #[inline]
+    pub const fn splat(v: bool) -> Self {
+        Self::new(v, v, v)
+    }
+
+    /// Returns a bitmask with the lowest 3 bits set from the elements of `self`.
     ///
     /// 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.
diff --git a/src/bool/sse2/bvec4a.rs b/src/bool/sse2/bvec4a.rs
index 73e27d5..d84f184 100644
--- a/src/bool/sse2/bvec4a.rs
+++ b/src/bool/sse2/bvec4a.rs
@@ -43,7 +43,13 @@ impl BVec4A {
         }
     }
 
-    /// Returns a bitmask with the lowest two bits set from the elements of `self`.
+    /// Creates a vector with all elements set to `v`.
+    #[inline]
+    pub const fn splat(v: bool) -> Self {
+        Self::new(v, v, v, v)
+    }
+
+    /// Returns a bitmask with the lowest 4 bits set from the elements of `self`.
     ///
     /// 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.
diff --git a/src/bool/wasm32/bvec3a.rs b/src/bool/wasm32/bvec3a.rs
index 9d343f2..5ff1906 100644
--- a/src/bool/wasm32/bvec3a.rs
+++ b/src/bool/wasm32/bvec3a.rs
@@ -30,7 +30,13 @@ impl BVec3A {
         ))
     }
 
-    /// Returns a bitmask with the lowest two bits set from the elements of `self`.
+    /// Creates a vector with all elements set to `v`.
+    #[inline]
+    pub const fn splat(v: bool) -> Self {
+        Self::new(v, v, v)
+    }
+
+    /// Returns a bitmask with the lowest 3 bits set from the elements of `self`.
     ///
     /// 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.
diff --git a/src/bool/wasm32/bvec4a.rs b/src/bool/wasm32/bvec4a.rs
index 555570e..d4c5c94 100644
--- a/src/bool/wasm32/bvec4a.rs
+++ b/src/bool/wasm32/bvec4a.rs
@@ -30,7 +30,13 @@ impl BVec4A {
         ))
     }
 
-    /// Returns a bitmask with the lowest two bits set from the elements of `self`.
+    /// Creates a vector with all elements set to `v`.
+    #[inline]
+    pub const fn splat(v: bool) -> Self {
+        Self::new(v, v, v, v)
+    }
+
+    /// Returns a bitmask with the lowest 4 bits set from the elements of `self`.
     ///
     /// 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.
diff --git a/src/f32/coresimd/vec3a.rs b/src/f32/coresimd/vec3a.rs
index 5215abb..966217e 100644
--- a/src/f32/coresimd/vec3a.rs
+++ b/src/f32/coresimd/vec3a.rs
@@ -148,6 +148,12 @@ impl Vec3A {
         dot3(self.0, rhs.0)
     }
 
+    /// Returns a vector where every component is the dot product of `self` and `rhs`
+    #[inline]
+    pub fn dot_into_vec(self, rhs: Self) -> Self {
+        Self(unsafe { dot3_into_f32x4(self.0, rhs.0) })
+    }
+
     /// Computes the cross product of `self` and `rhs`.
     #[inline]
     pub fn cross(self, rhs: Self) -> Self {
diff --git a/src/f32/coresimd/vec4.rs b/src/f32/coresimd/vec4.rs
index 03e5abe..38b2d4f 100644
--- a/src/f32/coresimd/vec4.rs
+++ b/src/f32/coresimd/vec4.rs
@@ -141,6 +141,12 @@ impl Vec4 {
         dot4(self.0, rhs.0)
     }
 
+    /// Returns a vector where every component is the dot product of `self` and `rhs`
+    #[inline]
+    pub fn dot_into_vec(self, rhs: Self) -> Self {
+        Self(unsafe { dot4_into_f32x4(self.0, rhs.0) })
+    }
+
     /// Returns a vector containing the minimum values for each element of `self` and `rhs`.
     ///
     /// In other words this computes `[self.x.min(rhs.x), self.y.min(rhs.y), ..]`.
diff --git a/src/f32/scalar/vec3a.rs b/src/f32/scalar/vec3a.rs
index 71b9ebd..00c1e8f 100644
--- a/src/f32/scalar/vec3a.rs
+++ b/src/f32/scalar/vec3a.rs
@@ -159,6 +159,12 @@ impl Vec3A {
         (self.x * rhs.x) + (self.y * rhs.y) + (self.z * rhs.z)
     }
 
+    /// Returns a vector where every component is the dot product of `self` and `rhs`
+    #[inline]
+    pub fn dot_into_vec(self, rhs: Self) -> Self {
+        Self::splat(self.dot(rhs))
+    }
+
     /// Computes the cross product of `self` and `rhs`.
     #[inline]
     pub fn cross(self, rhs: Self) -> Self {
diff --git a/src/f32/scalar/vec4.rs b/src/f32/scalar/vec4.rs
index 7facd6c..db0a60a 100644
--- a/src/f32/scalar/vec4.rs
+++ b/src/f32/scalar/vec4.rs
@@ -162,6 +162,12 @@ impl Vec4 {
         (self.x * rhs.x) + (self.y * rhs.y) + (self.z * rhs.z) + (self.w * rhs.w)
     }
 
+    /// Returns a vector where every component is the dot product of `self` and `rhs`
+    #[inline]
+    pub fn dot_into_vec(self, rhs: Self) -> Self {
+        Self::splat(self.dot(rhs))
+    }
+
     /// Returns a vector containing the minimum values for each element of `self` and `rhs`.
     ///
     /// In other words this computes `[self.x.min(rhs.x), self.y.min(rhs.y), ..]`.
diff --git a/src/f32/sse2/vec3a.rs b/src/f32/sse2/vec3a.rs
index 1ead0df..ace0933 100644
--- a/src/f32/sse2/vec3a.rs
+++ b/src/f32/sse2/vec3a.rs
@@ -160,6 +160,12 @@ impl Vec3A {
         unsafe { dot3(self.0, rhs.0) }
     }
 
+    /// Returns a vector where every component is the dot product of `self` and `rhs`
+    #[inline]
+    pub fn dot_into_vec(self, rhs: Self) -> Self {
+        Self(unsafe { dot3_into_m128(self.0, rhs.0) })
+    }
+
     /// Computes the cross product of `self` and `rhs`.
     #[inline]
     pub fn cross(self, rhs: Self) -> Self {
@@ -306,10 +312,11 @@ impl Vec3A {
     /// - `NAN` if the number is `NAN`
     #[inline]
     pub fn signum(self) -> Self {
-        let mask = self.cmpge(Self::ZERO);
-        let result = Self::select(mask, Self::ONE, Self::NEG_ONE);
-        let mask = self.is_nan_mask();
-        Self::select(mask, self, result)
+        unsafe {
+            let result = Self(_mm_or_ps(_mm_and_ps(self.0, Self::NEG_ONE.0), Self::ONE.0));
+            let mask = self.is_nan_mask();
+            Self::select(mask, self, result)
+        }
     }
 
     /// Returns `true` if, and only if, all elements are finite.  If any element is either
@@ -987,7 +994,7 @@ impl Neg for Vec3A {
     type Output = Self;
     #[inline]
     fn neg(self) -> Self {
-        Self(unsafe { _mm_sub_ps(Self::ZERO.0, self.0) })
+        Self(unsafe { _mm_xor_ps(_mm_set1_ps(-0.0), self.0) })
     }
 }
 
diff --git a/src/f32/sse2/vec4.rs b/src/f32/sse2/vec4.rs
index ab645ca..906f78a 100644
--- a/src/f32/sse2/vec4.rs
+++ b/src/f32/sse2/vec4.rs
@@ -153,6 +153,12 @@ impl Vec4 {
         unsafe { dot4(self.0, rhs.0) }
     }
 
+    /// Returns a vector where every component is the dot product of `self` and `rhs`
+    #[inline]
+    pub fn dot_into_vec(self, rhs: Self) -> Self {
+        Self(unsafe { dot4_into_m128(self.0, rhs.0) })
+    }
+
     /// Returns a vector containing the minimum values for each element of `self` and `rhs`.
     ///
     /// In other words this computes `[self.x.min(rhs.x), self.y.min(rhs.y), ..]`.
@@ -281,10 +287,11 @@ impl Vec4 {
     /// - `NAN` if the number is `NAN`
     #[inline]
     pub fn signum(self) -> Self {
-        let mask = self.cmpge(Self::ZERO);
-        let result = Self::select(mask, Self::ONE, Self::NEG_ONE);
-        let mask = self.is_nan_mask();
-        Self::select(mask, self, result)
+        unsafe {
+            let result = Self(_mm_or_ps(_mm_and_ps(self.0, Self::NEG_ONE.0), Self::ONE.0));
+            let mask = self.is_nan_mask();
+            Self::select(mask, self, result)
+        }
     }
 
     /// Returns `true` if, and only if, all elements are finite.  If any element is either
@@ -900,7 +907,7 @@ impl Neg for Vec4 {
     type Output = Self;
     #[inline]
     fn neg(self) -> Self {
-        Self(unsafe { _mm_sub_ps(Self::ZERO.0, self.0) })
+        Self(unsafe { _mm_xor_ps(_mm_set1_ps(-0.0), self.0) })
     }
 }
 
diff --git a/src/f32/vec2.rs b/src/f32/vec2.rs
index 7c275eb..ab9ec46 100644
--- a/src/f32/vec2.rs
+++ b/src/f32/vec2.rs
@@ -125,6 +125,12 @@ impl Vec2 {
         (self.x * rhs.x) + (self.y * rhs.y)
     }
 
+    /// Returns a vector where every component is the dot product of `self` and `rhs`
+    #[inline]
+    pub fn dot_into_vec(self, rhs: Self) -> Self {
+        Self::splat(self.dot(rhs))
+    }
+
     /// Returns a vector containing the minimum values for each element of `self` and `rhs`.
     ///
     /// In other words this computes `[self.x.min(rhs.x), self.y.min(rhs.y), ..]`.
diff --git a/src/f32/vec3.rs b/src/f32/vec3.rs
index df0ae09..3f745c0 100644
--- a/src/f32/vec3.rs
+++ b/src/f32/vec3.rs
@@ -153,6 +153,12 @@ impl Vec3 {
         (self.x * rhs.x) + (self.y * rhs.y) + (self.z * rhs.z)
     }
 
+    /// Returns a vector where every component is the dot product of `self` and `rhs`
+    #[inline]
+    pub fn dot_into_vec(self, rhs: Self) -> Self {
+        Self::splat(self.dot(rhs))
+    }
+
     /// Computes the cross product of `self` and `rhs`.
     #[inline]
     pub fn cross(self, rhs: Self) -> Self {
diff --git a/src/f32/wasm32/vec3a.rs b/src/f32/wasm32/vec3a.rs
index cec3c79..4ee5d7e 100644
--- a/src/f32/wasm32/vec3a.rs
+++ b/src/f32/wasm32/vec3a.rs
@@ -152,6 +152,12 @@ impl Vec3A {
         dot3(self.0, rhs.0)
     }
 
+    /// Returns a vector where every component is the dot product of `self` and `rhs`
+    #[inline]
+    pub fn dot_into_vec(self, rhs: Self) -> Self {
+        Self(unsafe { dot3_into_v128(self.0, rhs.0) })
+    }
+
     /// Computes the cross product of `self` and `rhs`.
     #[inline]
     pub fn cross(self, rhs: Self) -> Self {
@@ -287,10 +293,11 @@ impl Vec3A {
     /// - `NAN` if the number is `NAN`
     #[inline]
     pub fn signum(self) -> Self {
-        let mask = self.cmpge(Self::ZERO);
-        let result = Self::select(mask, Self::ONE, Self::NEG_ONE);
-        let mask = self.is_nan_mask();
-        Self::select(mask, self, result)
+        unsafe {
+            let result = Self(v128_or(v128_and(self.0, Self::NEG_ONE.0), Self::ONE.0));
+            let mask = self.is_nan_mask();
+            Self::select(mask, self, result)
+        }
     }
 
     /// Returns `true` if, and only if, all elements are finite.  If any element is either
diff --git a/src/f32/wasm32/vec4.rs b/src/f32/wasm32/vec4.rs
index bc79cc8..1fc475b 100644
--- a/src/f32/wasm32/vec4.rs
+++ b/src/f32/wasm32/vec4.rs
@@ -145,6 +145,12 @@ impl Vec4 {
         dot4(self.0, rhs.0)
     }
 
+    /// Returns a vector where every component is the dot product of `self` and `rhs`
+    #[inline]
+    pub fn dot_into_vec(self, rhs: Self) -> Self {
+        Self(unsafe { dot4_into_v128(self.0, rhs.0) })
+    }
+
     /// Returns a vector containing the minimum values for each element of `self` and `rhs`.
     ///
     /// In other words this computes `[self.x.min(rhs.x), self.y.min(rhs.y), ..]`.
@@ -269,10 +275,11 @@ impl Vec4 {
     /// - `NAN` if the number is `NAN`
     #[inline]
     pub fn signum(self) -> Self {
-        let mask = self.cmpge(Self::ZERO);
-        let result = Self::select(mask, Self::ONE, Self::NEG_ONE);
-        let mask = self.is_nan_mask();
-        Self::select(mask, self, result)
+        unsafe {
+            let result = Self(v128_or(v128_and(self.0, Self::NEG_ONE.0), Self::ONE.0));
+            let mask = self.is_nan_mask();
+            Self::select(mask, self, result)
+        }
     }
 
     /// Returns `true` if, and only if, all elements are finite.  If any element is either
diff --git a/src/f64/dvec2.rs b/src/f64/dvec2.rs
index 205dca2..3fdfc44 100644
--- a/src/f64/dvec2.rs
+++ b/src/f64/dvec2.rs
@@ -125,6 +125,12 @@ impl DVec2 {
         (self.x * rhs.x) + (self.y * rhs.y)
     }
 
+    /// Returns a vector where every component is the dot product of `self` and `rhs`
+    #[inline]
+    pub fn dot_into_vec(self, rhs: Self) -> Self {
+        Self::splat(self.dot(rhs))
+    }
+
     /// Returns a vector containing the minimum values for each element of `self` and `rhs`.
     ///
     /// In other words this computes `[self.x.min(rhs.x), self.y.min(rhs.y), ..]`.
diff --git a/src/f64/dvec3.rs b/src/f64/dvec3.rs
index 2251a28..d7bc1a2 100644
--- a/src/f64/dvec3.rs
+++ b/src/f64/dvec3.rs
@@ -153,6 +153,12 @@ impl DVec3 {
         (self.x * rhs.x) + (self.y * rhs.y) + (self.z * rhs.z)
     }
 
+    /// Returns a vector where every component is the dot product of `self` and `rhs`
+    #[inline]
+    pub fn dot_into_vec(self, rhs: Self) -> Self {
+        Self::splat(self.dot(rhs))
+    }
+
     /// Computes the cross product of `self` and `rhs`.
     #[inline]
     pub fn cross(self, rhs: Self) -> Self {
diff --git a/src/f64/dvec4.rs b/src/f64/dvec4.rs
index 132b8a8..21a60ce 100644
--- a/src/f64/dvec4.rs
+++ b/src/f64/dvec4.rs
@@ -154,6 +154,12 @@ impl DVec4 {
         (self.x * rhs.x) + (self.y * rhs.y) + (self.z * rhs.z) + (self.w * rhs.w)
     }
 
+    /// Returns a vector where every component is the dot product of `self` and `rhs`
+    #[inline]
+    pub fn dot_into_vec(self, rhs: Self) -> Self {
+        Self::splat(self.dot(rhs))
+    }
+
     /// Returns a vector containing the minimum values for each element of `self` and `rhs`.
     ///
     /// In other words this computes `[self.x.min(rhs.x), self.y.min(rhs.y), ..]`.
diff --git a/src/i32/ivec2.rs b/src/i32/ivec2.rs
index 4ad537b..fc21896 100644
--- a/src/i32/ivec2.rs
+++ b/src/i32/ivec2.rs
@@ -119,6 +119,12 @@ impl IVec2 {
         (self.x * rhs.x) + (self.y * rhs.y)
     }
 
+    /// Returns a vector where every component is the dot product of `self` and `rhs`
+    #[inline]
+    pub fn dot_into_vec(self, rhs: Self) -> Self {
+        Self::splat(self.dot(rhs))
+    }
+
     /// Returns a vector containing the minimum values for each element of `self` and `rhs`.
     ///
     /// In other words this computes `[self.x.min(rhs.x), self.y.min(rhs.y), ..]`.
diff --git a/src/i32/ivec3.rs b/src/i32/ivec3.rs
index 0019b51..1d14108 100644
--- a/src/i32/ivec3.rs
+++ b/src/i32/ivec3.rs
@@ -147,6 +147,12 @@ impl IVec3 {
         (self.x * rhs.x) + (self.y * rhs.y) + (self.z * rhs.z)
     }
 
+    /// Returns a vector where every component is the dot product of `self` and `rhs`
+    #[inline]
+    pub fn dot_into_vec(self, rhs: Self) -> Self {
+        Self::splat(self.dot(rhs))
+    }
+
     /// Computes the cross product of `self` and `rhs`.
     #[inline]
     pub fn cross(self, rhs: Self) -> Self {
diff --git a/src/i32/ivec4.rs b/src/i32/ivec4.rs
index 53e90ac..449f1b9 100644
--- a/src/i32/ivec4.rs
+++ b/src/i32/ivec4.rs
@@ -148,6 +148,12 @@ impl IVec4 {
         (self.x * rhs.x) + (self.y * rhs.y) + (self.z * rhs.z) + (self.w * rhs.w)
     }
 
+    /// Returns a vector where every component is the dot product of `self` and `rhs`
+    #[inline]
+    pub fn dot_into_vec(self, rhs: Self) -> Self {
+        Self::splat(self.dot(rhs))
+    }
+
     /// Returns a vector containing the minimum values for each element of `self` and `rhs`.
     ///
     /// In other words this computes `[self.x.min(rhs.x), self.y.min(rhs.y), ..]`.
diff --git a/src/u32/uvec2.rs b/src/u32/uvec2.rs
index b422600..ac3d7df 100644
--- a/src/u32/uvec2.rs
+++ b/src/u32/uvec2.rs
@@ -110,6 +110,12 @@ impl UVec2 {
         (self.x * rhs.x) + (self.y * rhs.y)
     }
 
+    /// Returns a vector where every component is the dot product of `self` and `rhs`
+    #[inline]
+    pub fn dot_into_vec(self, rhs: Self) -> Self {
+        Self::splat(self.dot(rhs))
+    }
+
     /// Returns a vector containing the minimum values for each element of `self` and `rhs`.
     ///
     /// In other words this computes `[self.x.min(rhs.x), self.y.min(rhs.y), ..]`.
diff --git a/src/u32/uvec3.rs b/src/u32/uvec3.rs
index 18a5bbd..192886e 100644
--- a/src/u32/uvec3.rs
+++ b/src/u32/uvec3.rs
@@ -135,6 +135,12 @@ impl UVec3 {
         (self.x * rhs.x) + (self.y * rhs.y) + (self.z * rhs.z)
     }
 
+    /// Returns a vector where every component is the dot product of `self` and `rhs`
+    #[inline]
+    pub fn dot_into_vec(self, rhs: Self) -> Self {
+        Self::splat(self.dot(rhs))
+    }
+
     /// Computes the cross product of `self` and `rhs`.
     #[inline]
     pub fn cross(self, rhs: Self) -> Self {
diff --git a/src/u32/uvec4.rs b/src/u32/uvec4.rs
index aa07f0c..decccf3 100644
--- a/src/u32/uvec4.rs
+++ b/src/u32/uvec4.rs
@@ -133,6 +133,12 @@ impl UVec4 {
         (self.x * rhs.x) + (self.y * rhs.y) + (self.z * rhs.z) + (self.w * rhs.w)
     }
 
+    /// Returns a vector where every component is the dot product of `self` and `rhs`
+    #[inline]
+    pub fn dot_into_vec(self, rhs: Self) -> Self {
+        Self::splat(self.dot(rhs))
+    }
+
     /// Returns a vector containing the minimum values for each element of `self` and `rhs`.
     ///
     /// In other words this computes `[self.x.min(rhs.x), self.y.min(rhs.y), ..]`.


Test patch:
diff --git a/tests/vec2.rs b/tests/vec2.rs
index f165982..8a4163c 100644
--- a/tests/vec2.rs
+++ b/tests/vec2.rs
@@ -268,6 +268,11 @@ macro_rules! impl_vec2_tests {
             );
         });
 
+        glam_test!(test_mask_splat, {
+            assert_eq!($mask::splat(false), $mask::new(false, false));
+            assert_eq!($mask::splat(true), $mask::new(true, true));
+        });
+
         glam_test!(test_mask_bitmask, {
             assert_eq!($mask::new(false, false).bitmask(), 0b00);
             assert_eq!($mask::new(true, false).bitmask(), 0b01);
@@ -473,6 +478,8 @@ macro_rules! impl_vec2_signed_tests {
         glam_test!(test_neg, {
             let a = $new(1 as $t, 2 as $t);
             assert_eq!($new(-1 as $t, -2 as $t), (-a));
+            assert_eq!($new(-0.0 as $t, -0.0 as $t), -$new(0.0 as $t, 0.0 as $t));
+            assert_eq!($new(0.0 as $t, -0.0 as $t), -$new(-0.0 as $t, 0.0 as $t));
         });
 
         glam_test!(test_perp, {
@@ -554,6 +561,7 @@ macro_rules! impl_vec2_float_tests {
             assert_eq!(13.0, (-5.0 * x).distance(12.0 * y));
             assert_eq!(x, (2.0 * x).normalize());
             assert_eq!(1.0 * 3.0 + 2.0 * 4.0, $new(1.0, 2.0).dot($new(3.0, 4.0)));
+            assert_eq!($new(8.0, 8.0), $new(1.0, 2.0).dot_into_vec($new(4.0, 2.0)));
             assert_eq!(2.0 * 2.0 + 3.0 * 3.0, $new(2.0, 3.0).length_squared());
             assert_eq!(
                 (2.0 as $t * 2.0 + 3.0 * 3.0).sqrt(),
@@ -590,7 +598,7 @@ macro_rules! impl_vec2_float_tests {
 
         glam_test!(test_sign, {
             assert_eq!($vec2::ZERO.signum(), $vec2::ONE);
-            assert_eq!(-$vec2::ZERO.signum(), -$vec2::ONE);
+            assert_eq!((-$vec2::ZERO).signum(), -$vec2::ONE);
             assert_eq!($vec2::ONE.signum(), $vec2::ONE);
             assert_eq!((-$vec2::ONE).signum(), -$vec2::ONE);
             assert_eq!($vec2::splat(INFINITY).signum(), $vec2::ONE);
diff --git a/tests/vec3.rs b/tests/vec3.rs
index 80956d7..095264b 100644
--- a/tests/vec3.rs
+++ b/tests/vec3.rs
@@ -316,6 +316,11 @@ macro_rules! impl_vec3_tests {
             );
         });
 
+        glam_test!(test_mask_splat, {
+            assert_eq!($mask::splat(false), $mask::new(false, false, false));
+            assert_eq!($mask::splat(true), $mask::new(true, true, true));
+        });
+
         glam_test!(test_mask_bitmask, {
             assert_eq!($mask::new(false, false, false).bitmask(), 0b000);
             assert_eq!($mask::new(true, false, false).bitmask(), 0b001);
@@ -518,6 +523,14 @@ macro_rules! impl_vec3_signed_tests {
         glam_test!(test_neg, {
             let a = $new(1 as $t, 2 as $t, 3 as $t);
             assert_eq!((-1 as $t, -2 as $t, -3 as $t), (-a).into());
+            assert_eq!(
+                $new(-0.0 as $t, -0.0 as $t, -0.0 as $t),
+                -$new(0.0 as $t, 0.0 as $t, 0.0 as $t)
+            );
+            assert_eq!(
+                $new(0.0 as $t, -0.0 as $t, -0.0 as $t),
+                -$new(-0.0 as $t, 0.0 as $t, 0.0 as $t)
+            );
         });
 
         glam_test!(test_dot_signed, {
@@ -598,6 +611,10 @@ macro_rules! impl_vec3_float_tests {
                 1.0 * 4.0 + 2.0 * 5.0 + 3.0 * 6.0,
                 $new(1.0, 2.0, 3.0).dot($new(4.0, 5.0, 6.0))
             );
+            assert_eq!(
+                $new(14.0, 14.0, 14.0),
+                $new(0.0, 4.0, 6.0).dot_into_vec($new(3.0, 2.0, 1.0))
+            );
             assert_eq!(
                 2.0 * 2.0 + 3.0 * 3.0 + 4.0 * 4.0,
                 $new(2.0, 3.0, 4.0).length_squared()
@@ -643,7 +660,7 @@ macro_rules! impl_vec3_float_tests {
 
         glam_test!(test_signum, {
             assert_eq!($vec3::ZERO.signum(), $vec3::ONE);
-            assert_eq!(-$vec3::ZERO.signum(), -$vec3::ONE);
+            assert_eq!((-$vec3::ZERO).signum(), -$vec3::ONE);
             assert_eq!($vec3::ONE.signum(), $vec3::ONE);
             assert_eq!((-$vec3::ONE).signum(), -$vec3::ONE);
             assert_eq!($vec3::splat(INFINITY).signum(), $vec3::ONE);
diff --git a/tests/vec4.rs b/tests/vec4.rs
index 60b282a..2646727 100644
--- a/tests/vec4.rs
+++ b/tests/vec4.rs
@@ -386,6 +386,11 @@ macro_rules! impl_vec4_tests {
             );
         });
 
+        glam_test!(test_mask_splat, {
+            assert_eq!($mask::splat(false), $mask::new(false, false, false, false));
+            assert_eq!($mask::splat(true), $mask::new(true, true, true, true));
+        });
+
         glam_test!(test_mask_bitmask, {
             assert_eq!($mask::new(false, false, false, false).bitmask(), 0b0000);
             assert_eq!($mask::new(false, false, true, true).bitmask(), 0b1100);
@@ -593,6 +598,14 @@ macro_rules! impl_vec4_signed_tests {
         glam_test!(test_neg, {
             let a = $new(1 as $t, 2 as $t, 3 as $t, 4 as $t);
             assert_eq!((-1 as $t, -2 as $t, -3 as $t, -4 as $t), (-a).into());
+            assert_eq!(
+                $new(-0.0 as $t, -0.0 as $t, -0.0 as $t, -0.0 as $t),
+                -$new(0.0 as $t, 0.0 as $t, 0.0 as $t, 0.0 as $t)
+            );
+            assert_eq!(
+                $new(0.0 as $t, -0.0 as $t, -0.0 as $t, -0.0 as $t),
+                -$new(-0.0 as $t, 0.0 as $t, 0.0 as $t, 0.0 as $t)
+            );
         });
 
         glam_test!(test_dot_signed, {
@@ -676,6 +689,10 @@ macro_rules! impl_vec4_float_tests {
                 1.0 * 5.0 + 2.0 * 6.0 + 3.0 * 7.0 + 4.0 * 8.0,
                 $new(1.0, 2.0, 3.0, 4.0).dot($new(5.0, 6.0, 7.0, 8.0))
             );
+            assert_eq!(
+                $new(28.0, 28.0, 28.0, 28.0),
+                $new(0.0, 5.0, 3.0, 6.0).dot_into_vec($new(7.0, 2.0, 4.0, 1.0))
+            );
             assert_eq!(
                 2.0 * 2.0 + 3.0 * 3.0 + 4.0 * 4.0 + 5.0 * 5.0,
                 $new(2.0, 3.0, 4.0, 5.0).length_squared()
@@ -725,7 +742,7 @@ macro_rules! impl_vec4_float_tests {
 
         glam_test!(test_signum, {
             assert_eq!($vec4::ZERO.signum(), $vec4::ONE);
-            assert_eq!(-$vec4::ZERO.signum(), -$vec4::ONE);
+            assert_eq!((-$vec4::ZERO).signum(), -$vec4::ONE);
             assert_eq!($vec4::ONE.signum(), $vec4::ONE);
             assert_eq!((-$vec4::ONE).signum(), -$vec4::ONE);
             assert_eq!($vec4::splat(INFINITY).signum(), $vec4::ONE);