Skip to content

Commit

Permalink
Add Vec::sign_bits (#345)
Browse files Browse the repository at this point in the history
* Fix negation and signum to consistently handle negative zero across platforms

* Add sign_bits and tests

* rustfmt

* Try to fix coresimd

* rename sign_bits to is_negative_bitmask

* rustfmt
  • Loading branch information
atlv24 committed Oct 1, 2022
1 parent 4cc6967 commit 30f1788
Show file tree
Hide file tree
Showing 20 changed files with 254 additions and 0 deletions.
43 changes: 43 additions & 0 deletions codegen/templates/vec.rs.tera
Expand Up @@ -825,6 +825,49 @@ impl {{ self_t }} {
}
{% endif %}
}

/// Returns a bitmask with the lowest {{ dim }} bits set to the sign bits from the elements of `self`.
///
/// A negative element results in a `1` bit and a positive element in a `0` bit. Element `x` goes
/// into the first lowest bit, element `y` into the second, etc.
#[inline]
pub fn is_negative_bitmask(self) -> u32 {
{% if is_scalar and is_float %}
{% for c in components %}
{% if loop.first %}
(self.{{ c }}.is_sign_negative() as u32) |
{% else %}
(self.{{ c }}.is_sign_negative() as u32) << {{ loop.index0 }} {% if not loop.last %} | {% endif %}
{% endif %}
{% endfor %}
{% elif is_scalar %}
{% for c in components %}
{% if loop.first %}
(self.{{ c }}.is_negative() as u32) |
{% else %}
(self.{{ c }}.is_negative() as u32) << {{ loop.index0 }} {% if not loop.last %} | {% endif %}
{% endif %}
{% endfor %}
{% elif is_sse2 %}
{% if dim == 3 %}
unsafe { (_mm_movemask_ps(self.0) as u32) & 0x7 }
{% elif dim == 4 %}
unsafe { _mm_movemask_ps(self.0) as u32 }
{% endif %}
{% elif is_wasm32 %}
{% if dim == 3 %}
(u32x4_bitmask(self.0) & 0x7) as u32
{% elif dim == 4 %}
u32x4_bitmask(self.0) as u32
{% endif %}
{% elif is_coresimd %}
{% if dim == 3 %}
(self.0.is_sign_negative().to_bitmask() & 0x7) as u32
{% elif dim == 4 %}
self.0.is_sign_negative().to_bitmask() as u32
{% endif %}
{% endif %}
}
{% endif %}

{% if is_float %}
Expand Down
9 changes: 9 additions & 0 deletions src/f32/coresimd/vec3a.rs
Expand Up @@ -292,6 +292,15 @@ impl Vec3A {
Self(self.0.signum())
}

/// Returns a bitmask with the lowest 3 bits set to the sign bits from the elements of `self`.
///
/// A negative element results in a `1` bit and a positive element in a `0` bit. Element `x` goes
/// into the first lowest bit, element `y` into the second, etc.
#[inline]
pub fn is_negative_bitmask(self) -> u32 {
(self.0.is_sign_negative().to_bitmask() & 0x7) as u32
}

/// Returns `true` if, and only if, all elements are finite. If any element is either
/// `NaN`, positive or negative infinity, this will return `false`.
#[inline]
Expand Down
9 changes: 9 additions & 0 deletions src/f32/coresimd/vec4.rs
Expand Up @@ -268,6 +268,15 @@ impl Vec4 {
Self(self.0.signum())
}

/// Returns a bitmask with the lowest 4 bits set to the sign bits from the elements of `self`.
///
/// A negative element results in a `1` bit and a positive element in a `0` bit. Element `x` goes
/// into the first lowest bit, element `y` into the second, etc.
#[inline]
pub fn is_negative_bitmask(self) -> u32 {
self.0.is_sign_negative().to_bitmask() as u32
}

/// Returns `true` if, and only if, all elements are finite. If any element is either
/// `NaN`, positive or negative infinity, this will return `false`.
#[inline]
Expand Down
11 changes: 11 additions & 0 deletions src/f32/scalar/vec3a.rs
Expand Up @@ -312,6 +312,17 @@ impl Vec3A {
}
}

/// Returns a bitmask with the lowest 3 bits set to the sign bits from the elements of `self`.
///
/// A negative element results in a `1` bit and a positive element in a `0` bit. Element `x` goes
/// into the first lowest bit, element `y` into the second, etc.
#[inline]
pub fn is_negative_bitmask(self) -> u32 {
(self.x.is_sign_negative() as u32)
| (self.y.is_sign_negative() as u32) << 1
| (self.z.is_sign_negative() as u32) << 2
}

/// Returns `true` if, and only if, all elements are finite. If any element is either
/// `NaN`, positive or negative infinity, this will return `false`.
#[inline]
Expand Down
12 changes: 12 additions & 0 deletions src/f32/scalar/vec4.rs
Expand Up @@ -339,6 +339,18 @@ impl Vec4 {
}
}

/// Returns a bitmask with the lowest 4 bits set to the sign bits from the elements of `self`.
///
/// A negative element results in a `1` bit and a positive element in a `0` bit. Element `x` goes
/// into the first lowest bit, element `y` into the second, etc.
#[inline]
pub fn is_negative_bitmask(self) -> u32 {
(self.x.is_sign_negative() as u32)
| (self.y.is_sign_negative() as u32) << 1
| (self.z.is_sign_negative() as u32) << 2
| (self.w.is_sign_negative() as u32) << 3
}

/// Returns `true` if, and only if, all elements are finite. If any element is either
/// `NaN`, positive or negative infinity, this will return `false`.
#[inline]
Expand Down
9 changes: 9 additions & 0 deletions src/f32/sse2/vec3a.rs
Expand Up @@ -319,6 +319,15 @@ impl Vec3A {
}
}

/// Returns a bitmask with the lowest 3 bits set to the sign bits from the elements of `self`.
///
/// A negative element results in a `1` bit and a positive element in a `0` bit. Element `x` goes
/// into the first lowest bit, element `y` into the second, etc.
#[inline]
pub fn is_negative_bitmask(self) -> u32 {
unsafe { (_mm_movemask_ps(self.0) as u32) & 0x7 }
}

/// Returns `true` if, and only if, all elements are finite. If any element is either
/// `NaN`, positive or negative infinity, this will return `false`.
#[inline]
Expand Down
9 changes: 9 additions & 0 deletions src/f32/sse2/vec4.rs
Expand Up @@ -294,6 +294,15 @@ impl Vec4 {
}
}

/// Returns a bitmask with the lowest 4 bits set to the sign bits from the elements of `self`.
///
/// A negative element results in a `1` bit and a positive element in a `0` bit. Element `x` goes
/// into the first lowest bit, element `y` into the second, etc.
#[inline]
pub fn is_negative_bitmask(self) -> u32 {
unsafe { _mm_movemask_ps(self.0) as u32 }
}

/// Returns `true` if, and only if, all elements are finite. If any element is either
/// `NaN`, positive or negative infinity, this will return `false`.
#[inline]
Expand Down
9 changes: 9 additions & 0 deletions src/f32/vec2.rs
Expand Up @@ -264,6 +264,15 @@ impl Vec2 {
}
}

/// Returns a bitmask with the lowest 2 bits set to the sign bits from the elements of `self`.
///
/// A negative element results in a `1` bit and a positive element in a `0` bit. Element `x` goes
/// into the first lowest bit, element `y` into the second, etc.
#[inline]
pub fn is_negative_bitmask(self) -> u32 {
(self.x.is_sign_negative() as u32) | (self.y.is_sign_negative() as u32) << 1
}

/// Returns `true` if, and only if, all elements are finite. If any element is either
/// `NaN`, positive or negative infinity, this will return `false`.
#[inline]
Expand Down
11 changes: 11 additions & 0 deletions src/f32/vec3.rs
Expand Up @@ -306,6 +306,17 @@ impl Vec3 {
}
}

/// Returns a bitmask with the lowest 3 bits set to the sign bits from the elements of `self`.
///
/// A negative element results in a `1` bit and a positive element in a `0` bit. Element `x` goes
/// into the first lowest bit, element `y` into the second, etc.
#[inline]
pub fn is_negative_bitmask(self) -> u32 {
(self.x.is_sign_negative() as u32)
| (self.y.is_sign_negative() as u32) << 1
| (self.z.is_sign_negative() as u32) << 2
}

/// Returns `true` if, and only if, all elements are finite. If any element is either
/// `NaN`, positive or negative infinity, this will return `false`.
#[inline]
Expand Down
9 changes: 9 additions & 0 deletions src/f32/wasm32/vec3a.rs
Expand Up @@ -300,6 +300,15 @@ impl Vec3A {
}
}

/// Returns a bitmask with the lowest 3 bits set to the sign bits from the elements of `self`.
///
/// A negative element results in a `1` bit and a positive element in a `0` bit. Element `x` goes
/// into the first lowest bit, element `y` into the second, etc.
#[inline]
pub fn is_negative_bitmask(self) -> u32 {
(u32x4_bitmask(self.0) & 0x7) as u32
}

/// Returns `true` if, and only if, all elements are finite. If any element is either
/// `NaN`, positive or negative infinity, this will return `false`.
#[inline]
Expand Down
9 changes: 9 additions & 0 deletions src/f32/wasm32/vec4.rs
Expand Up @@ -282,6 +282,15 @@ impl Vec4 {
}
}

/// Returns a bitmask with the lowest 4 bits set to the sign bits from the elements of `self`.
///
/// A negative element results in a `1` bit and a positive element in a `0` bit. Element `x` goes
/// into the first lowest bit, element `y` into the second, etc.
#[inline]
pub fn is_negative_bitmask(self) -> u32 {
u32x4_bitmask(self.0) as u32
}

/// Returns `true` if, and only if, all elements are finite. If any element is either
/// `NaN`, positive or negative infinity, this will return `false`.
#[inline]
Expand Down
9 changes: 9 additions & 0 deletions src/f64/dvec2.rs
Expand Up @@ -264,6 +264,15 @@ impl DVec2 {
}
}

/// Returns a bitmask with the lowest 2 bits set to the sign bits from the elements of `self`.
///
/// A negative element results in a `1` bit and a positive element in a `0` bit. Element `x` goes
/// into the first lowest bit, element `y` into the second, etc.
#[inline]
pub fn is_negative_bitmask(self) -> u32 {
(self.x.is_sign_negative() as u32) | (self.y.is_sign_negative() as u32) << 1
}

/// Returns `true` if, and only if, all elements are finite. If any element is either
/// `NaN`, positive or negative infinity, this will return `false`.
#[inline]
Expand Down
11 changes: 11 additions & 0 deletions src/f64/dvec3.rs
Expand Up @@ -306,6 +306,17 @@ impl DVec3 {
}
}

/// Returns a bitmask with the lowest 3 bits set to the sign bits from the elements of `self`.
///
/// A negative element results in a `1` bit and a positive element in a `0` bit. Element `x` goes
/// into the first lowest bit, element `y` into the second, etc.
#[inline]
pub fn is_negative_bitmask(self) -> u32 {
(self.x.is_sign_negative() as u32)
| (self.y.is_sign_negative() as u32) << 1
| (self.z.is_sign_negative() as u32) << 2
}

/// Returns `true` if, and only if, all elements are finite. If any element is either
/// `NaN`, positive or negative infinity, this will return `false`.
#[inline]
Expand Down
12 changes: 12 additions & 0 deletions src/f64/dvec4.rs
Expand Up @@ -331,6 +331,18 @@ impl DVec4 {
}
}

/// Returns a bitmask with the lowest 4 bits set to the sign bits from the elements of `self`.
///
/// A negative element results in a `1` bit and a positive element in a `0` bit. Element `x` goes
/// into the first lowest bit, element `y` into the second, etc.
#[inline]
pub fn is_negative_bitmask(self) -> u32 {
(self.x.is_sign_negative() as u32)
| (self.y.is_sign_negative() as u32) << 1
| (self.z.is_sign_negative() as u32) << 2
| (self.w.is_sign_negative() as u32) << 3
}

/// Returns `true` if, and only if, all elements are finite. If any element is either
/// `NaN`, positive or negative infinity, this will return `false`.
#[inline]
Expand Down
9 changes: 9 additions & 0 deletions src/i32/ivec2.rs
Expand Up @@ -258,6 +258,15 @@ impl IVec2 {
}
}

/// Returns a bitmask with the lowest 2 bits set to the sign bits from the elements of `self`.
///
/// A negative element results in a `1` bit and a positive element in a `0` bit. Element `x` goes
/// into the first lowest bit, element `y` into the second, etc.
#[inline]
pub fn is_negative_bitmask(self) -> u32 {
(self.x.is_negative() as u32) | (self.y.is_negative() as u32) << 1
}

/// Returns a vector that is equal to `self` rotated by 90 degrees.
#[inline]
pub fn perp(self) -> Self {
Expand Down
11 changes: 11 additions & 0 deletions src/i32/ivec3.rs
Expand Up @@ -300,6 +300,17 @@ impl IVec3 {
}
}

/// Returns a bitmask with the lowest 3 bits set to the sign bits from the elements of `self`.
///
/// A negative element results in a `1` bit and a positive element in a `0` bit. Element `x` goes
/// into the first lowest bit, element `y` into the second, etc.
#[inline]
pub fn is_negative_bitmask(self) -> u32 {
(self.x.is_negative() as u32)
| (self.y.is_negative() as u32) << 1
| (self.z.is_negative() as u32) << 2
}

/// Casts all elements of `self` to `f32`.
#[inline]
pub fn as_vec3(&self) -> crate::Vec3 {
Expand Down
12 changes: 12 additions & 0 deletions src/i32/ivec4.rs
Expand Up @@ -325,6 +325,18 @@ impl IVec4 {
}
}

/// Returns a bitmask with the lowest 4 bits set to the sign bits from the elements of `self`.
///
/// A negative element results in a `1` bit and a positive element in a `0` bit. Element `x` goes
/// into the first lowest bit, element `y` into the second, etc.
#[inline]
pub fn is_negative_bitmask(self) -> u32 {
(self.x.is_negative() as u32)
| (self.y.is_negative() as u32) << 1
| (self.z.is_negative() as u32) << 2
| (self.w.is_negative() as u32) << 3
}

/// Casts all elements of `self` to `f32`.
#[inline]
pub fn as_vec4(&self) -> crate::Vec4 {
Expand Down
11 changes: 11 additions & 0 deletions tests/vec2.rs
Expand Up @@ -606,6 +606,17 @@ macro_rules! impl_vec2_float_tests {
assert!($vec2::splat(NAN).signum().is_nan_mask().all());
});

glam_test!(test_is_negative_bitmask, {
assert_eq!($vec2::ZERO.is_negative_bitmask(), 0b00);
assert_eq!((-$vec2::ZERO).is_negative_bitmask(), 0b11);
assert_eq!($vec2::ONE.is_negative_bitmask(), 0b00);
assert_eq!((-$vec2::ONE).is_negative_bitmask(), 0b11);
assert_eq!($vec2::new(-0.1, 0.2).is_negative_bitmask(), 0b01);
assert_eq!($vec2::new(0.8, 0.3).is_negative_bitmask(), 0b00);
assert_eq!($vec2::new(0.3, -0.4).is_negative_bitmask(), 0b10);
assert_eq!($vec2::new(-0.2, -0.6).is_negative_bitmask(), 0b11);
});

glam_test!(test_abs, {
assert_eq!($vec2::ZERO.abs(), $vec2::ZERO);
assert_eq!($vec2::ONE.abs(), $vec2::ONE);
Expand Down
12 changes: 12 additions & 0 deletions tests/vec3.rs
Expand Up @@ -668,6 +668,18 @@ macro_rules! impl_vec3_float_tests {
assert!($vec3::splat(NAN).signum().is_nan_mask().all());
});

glam_test!(test_is_negative_bitmask, {
assert_eq!($vec3::ZERO.is_negative_bitmask(), 0b000);
assert_eq!((-$vec3::ZERO).is_negative_bitmask(), 0b111);
assert_eq!($vec3::ONE.is_negative_bitmask(), 0b000);
assert_eq!((-$vec3::ONE).is_negative_bitmask(), 0b111);
assert_eq!($vec3::new(-0.1, 0.2, 0.3).is_negative_bitmask(), 0b001);
assert_eq!($vec3::new(0.8, 0.3, 0.1).is_negative_bitmask(), 0b000);
assert_eq!($vec3::new(0.1, 0.5, -0.3).is_negative_bitmask(), 0b100);
assert_eq!($vec3::new(0.3, -0.4, 0.1).is_negative_bitmask(), 0b010);
assert_eq!($vec3::new(-0.2, 0.6, -0.5).is_negative_bitmask(), 0b101);
});

glam_test!(test_abs, {
assert_eq!($vec3::ZERO.abs(), $vec3::ZERO);
assert_eq!($vec3::ONE.abs(), $vec3::ONE);
Expand Down
27 changes: 27 additions & 0 deletions tests/vec4.rs
Expand Up @@ -750,6 +750,33 @@ macro_rules! impl_vec4_float_tests {
assert!($vec4::splat(NAN).signum().is_nan_mask().all());
});

glam_test!(test_is_negative_bitmask, {
assert_eq!($vec4::ZERO.is_negative_bitmask(), 0b0000);
assert_eq!((-$vec4::ZERO).is_negative_bitmask(), 0b1111);
assert_eq!($vec4::ONE.is_negative_bitmask(), 0b0000);
assert_eq!((-$vec4::ONE).is_negative_bitmask(), 0b1111);
assert_eq!(
$vec4::new(-0.1, 0.2, 0.3, -0.4).is_negative_bitmask(),
0b1001
);
assert_eq!(
$vec4::new(0.8, 0.3, 0.1, -0.0).is_negative_bitmask(),
0b1000
);
assert_eq!(
$vec4::new(0.1, 0.5, -0.3, 0.7).is_negative_bitmask(),
0b0100
);
assert_eq!(
$vec4::new(0.3, -0.4, 0.1, 0.6).is_negative_bitmask(),
0b0010
);
assert_eq!(
$vec4::new(0.2, -0.6, 0.5, -0.3).is_negative_bitmask(),
0b1010
);
});

glam_test!(test_abs, {
assert_eq!($vec4::ZERO.abs(), $vec4::ZERO);
assert_eq!($vec4::ONE.abs(), $vec4::ONE);
Expand Down

0 comments on commit 30f1788

Please sign in to comment.