aboutsummaryrefslogtreecommitdiff
path: root/src/value.zig
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2022-03-17 17:24:35 -0700
committerAndrew Kelley <andrew@ziglang.org>2022-03-17 17:24:35 -0700
commit7233a3324aaa5b3995606f24b2b961149219986b (patch)
tree009a83fa10f489f0d6da4f77f07a4d0c8ca4e388 /src/value.zig
parent76e103057ea6037d3bb3e44cd33880a9a91609fb (diff)
downloadzig-7233a3324aaa5b3995606f24b2b961149219986b.tar.gz
zig-7233a3324aaa5b3995606f24b2b961149219986b.zip
stage2: implement `@reduce`
Notably, Value.eql and Value.hash are improved to treat NaN as equal to itself, so that Type/Value can be hash map keys. Likewise float hashing normalizes the float value before computing the hash.
Diffstat (limited to 'src/value.zig')
-rw-r--r--src/value.zig30
1 files changed, 25 insertions, 5 deletions
diff --git a/src/value.zig b/src/value.zig
index 005f1f1ffd..b45c106c26 100644
--- a/src/value.zig
+++ b/src/value.zig
@@ -1841,6 +1841,8 @@ pub const Value = extern union {
return orderAgainstZero(lhs).compare(op);
}
+ /// This function is used by hash maps and so treats floating-point NaNs as equal
+ /// to each other, and not equal to other floating-point values.
pub fn eql(a: Value, b: Value, ty: Type) bool {
const a_tag = a.tag();
const b_tag = b.tag();
@@ -2006,10 +2008,20 @@ pub const Value = extern union {
// end up here and the values are equal if the type has zero fields.
return ty.structFieldCount() != 0;
},
+ .Float => {
+ const a_nan = a.isNan();
+ const b_nan = b.isNan();
+ if (a_nan or b_nan) {
+ return a_nan and b_nan;
+ }
+ return order(a, b).compare(.eq);
+ },
else => return order(a, b).compare(.eq),
}
}
+ /// This function is used by hash maps and so treats floating-point NaNs as equal
+ /// to each other, and not equal to other floating-point values.
pub fn hash(val: Value, ty: Type, hasher: *std.hash.Wyhash) void {
const zig_ty_tag = ty.zigTypeTag();
std.hash.autoHash(hasher, zig_ty_tag);
@@ -2030,10 +2042,18 @@ pub const Value = extern union {
return val.toType(&buf).hashWithHasher(hasher);
},
.Float, .ComptimeFloat => {
- // TODO double check the lang spec. should we to bitwise hashing here,
- // or a hash that normalizes the float value?
+ // Normalize the float here because this hash must match eql semantics.
+ // These functions are used for hash maps so we want NaN to equal itself,
+ // and -0.0 to equal +0.0.
const float = val.toFloat(f128);
- std.hash.autoHash(hasher, @bitCast(u128, float));
+ if (std.math.isNan(float)) {
+ std.hash.autoHash(hasher, std.math.nan_u128);
+ } else if (float == 0.0) {
+ var normalized_zero: f128 = 0.0;
+ std.hash.autoHash(hasher, @bitCast(u128, normalized_zero));
+ } else {
+ std.hash.autoHash(hasher, @bitCast(u128, float));
+ }
},
.Bool, .Int, .ComptimeInt, .Pointer => switch (val.tag()) {
.slice => {
@@ -2948,7 +2968,7 @@ pub const Value = extern union {
}
/// Supports both floats and ints; handles undefined.
- pub fn numberMax(lhs: Value, rhs: Value) !Value {
+ pub fn numberMax(lhs: Value, rhs: Value) Value {
if (lhs.isUndef() or rhs.isUndef()) return undef;
if (lhs.isNan()) return rhs;
if (rhs.isNan()) return lhs;
@@ -2960,7 +2980,7 @@ pub const Value = extern union {
}
/// Supports both floats and ints; handles undefined.
- pub fn numberMin(lhs: Value, rhs: Value) !Value {
+ pub fn numberMin(lhs: Value, rhs: Value) Value {
if (lhs.isUndef() or rhs.isUndef()) return undef;
if (lhs.isNan()) return rhs;
if (rhs.isNan()) return lhs;