diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2022-03-17 17:24:35 -0700 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2022-03-17 17:24:35 -0700 |
| commit | 7233a3324aaa5b3995606f24b2b961149219986b (patch) | |
| tree | 009a83fa10f489f0d6da4f77f07a4d0c8ca4e388 /src/value.zig | |
| parent | 76e103057ea6037d3bb3e44cd33880a9a91609fb (diff) | |
| download | zig-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.zig | 30 |
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; |
