Skip to content

Simplify floating points comparison for Object.is #2476

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 28, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 15 additions & 14 deletions std/assembly/object.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
export class Object {
static is<T>(value1: T, value2: T): bool {
static is<T>(x: T, y: T): bool {
if (isFloat<T>()) {
if (value1 == value2) {
// 0 == -0, but they are not identical
if (sizeof<T>() == 8) {
// @ts-ignore: typecast
return reinterpret<u64>(value1) == reinterpret<u64>(value2);
} else {
// @ts-ignore: typecast
return reinterpret<u32>(value1) == reinterpret<u32>(value2);
}
// Float pointing is special we shoulr presere following identities:
// 0.0 !=-0.0
// NaN == NaN
if (sizeof<T>() == 8) {
return (
bool(u32(x != x) & u32(y != y) |
u32(reinterpret<u64>(f64(x)) == reinterpret<u64>(f64(y))))
);
} else {
return (
bool(u32(x != x) & u32(y != y) |
u32(reinterpret<u32>(f32(x)) == reinterpret<u32>(f32(y))))
);
}
// NaN != NaN, but they are identical.
// @ts-ignore: typecast
return bool(i32(isNaN(value1)) & i32(isNaN(value2)));
}
// For references, strings, integers and booleans
return value1 == value2;
return x == y;
}
}
152 changes: 100 additions & 52 deletions tests/compiler/std/object.debug.wat
Original file line number Diff line number Diff line change
@@ -24,74 +24,64 @@
(elem $0 (i32.const 1))
(export "memory" (memory $0))
(start $~start)
(func $~lib/object/Object.is<f64> (param $value1 f64) (param $value2 f64) (result i32)
(func $~lib/object/Object.is<f64> (param $x f64) (param $y f64) (result i32)
i32.const 1
drop
local.get $value1
local.get $value2
f64.eq
if
i32.const 8
i32.const 8
i32.eq
drop
local.get $value1
i64.reinterpret_f64
local.get $value2
i64.reinterpret_f64
i64.eq
return
end
local.get $value1
local.get $value1
i32.const 8
i32.const 8
i32.eq
drop
local.get $x
local.get $x
f64.ne
local.get $value2
local.get $value2
local.get $y
local.get $y
f64.ne
i32.and
local.get $x
i64.reinterpret_f64
local.get $y
i64.reinterpret_f64
i64.eq
i32.or
return
)
(func $~lib/object/Object.is<f32> (param $value1 f32) (param $value2 f32) (result i32)
(func $~lib/object/Object.is<f32> (param $x f32) (param $y f32) (result i32)
i32.const 1
drop
local.get $value1
local.get $value2
f32.eq
if
i32.const 4
i32.const 8
i32.eq
drop
local.get $value1
i32.reinterpret_f32
local.get $value2
i32.reinterpret_f32
i32.eq
return
end
local.get $value1
local.get $value1
i32.const 4
i32.const 8
i32.eq
drop
local.get $x
local.get $x
f32.ne
local.get $value2
local.get $value2
local.get $y
local.get $y
f32.ne
i32.and
local.get $x
i32.reinterpret_f32
local.get $y
i32.reinterpret_f32
i32.eq
i32.or
return
)
(func $~lib/object/Object.is<i32> (param $value1 i32) (param $value2 i32) (result i32)
(func $~lib/object/Object.is<i32> (param $x i32) (param $y i32) (result i32)
i32.const 0
drop
local.get $value1
local.get $value2
local.get $x
local.get $y
i32.eq
)
(func $~lib/object/Object.is<bool> (param $value1 i32) (param $value2 i32) (result i32)
(func $~lib/object/Object.is<bool> (param $x i32) (param $y i32) (result i32)
i32.const 0
drop
local.get $value1
local.get $x
i32.const 0
i32.ne
local.get $value2
local.get $y
i32.const 0
i32.ne
i32.eq
@@ -251,18 +241,18 @@
call $~lib/util/string/compareImpl
i32.eqz
)
(func $~lib/object/Object.is<~lib/string/String> (param $value1 i32) (param $value2 i32) (result i32)
(func $~lib/object/Object.is<~lib/string/String> (param $x i32) (param $y i32) (result i32)
i32.const 0
drop
local.get $value1
local.get $value2
local.get $x
local.get $y
call $~lib/string/String.__eq
)
(func $~lib/object/Object.is<~lib/string/String|null> (param $value1 i32) (param $value2 i32) (result i32)
(func $~lib/object/Object.is<~lib/string/String|null> (param $x i32) (param $y i32) (result i32)
i32.const 0
drop
local.get $value1
local.get $value2
local.get $x
local.get $y
call $~lib/string/String.__eq
)
(func $~start
@@ -294,6 +284,8 @@
f64.const 0
f64.const 0
call $~lib/object/Object.is<f64>
i32.const 0
i32.ne
i32.const 1
i32.eq
i32.eqz
@@ -309,6 +301,8 @@
f64.const -0
call $~lib/object/Object.is<f64>
i32.const 0
i32.ne
i32.const 0
i32.eq
i32.eqz
if
@@ -323,6 +317,8 @@
f64.const 0
call $~lib/object/Object.is<f64>
i32.const 0
i32.ne
i32.const 0
i32.eq
i32.eqz
if
@@ -337,6 +333,8 @@
f64.const 1
call $~lib/object/Object.is<f64>
i32.const 0
i32.ne
i32.const 0
i32.eq
i32.eqz
if
@@ -351,6 +349,8 @@
f64.const -1
call $~lib/object/Object.is<f64>
i32.const 0
i32.ne
i32.const 0
i32.eq
i32.eqz
if
@@ -364,6 +364,8 @@
f64.const 1
f64.const 1
call $~lib/object/Object.is<f64>
i32.const 0
i32.ne
i32.const 1
i32.eq
i32.eqz
@@ -378,6 +380,8 @@
f64.const inf
f64.const inf
call $~lib/object/Object.is<f64>
i32.const 0
i32.ne
i32.const 1
i32.eq
i32.eqz
@@ -394,6 +398,8 @@
f64.const inf
f64.neg
call $~lib/object/Object.is<f64>
i32.const 0
i32.ne
i32.const 1
i32.eq
i32.eqz
@@ -410,6 +416,8 @@
f64.neg
call $~lib/object/Object.is<f64>
i32.const 0
i32.ne
i32.const 0
i32.eq
i32.eqz
if
@@ -425,6 +433,8 @@
f64.const inf
call $~lib/object/Object.is<f64>
i32.const 0
i32.ne
i32.const 0
i32.eq
i32.eqz
if
@@ -439,6 +449,8 @@
f64.const nan:0x8000000000000
call $~lib/object/Object.is<f64>
i32.const 0
i32.ne
i32.const 0
i32.eq
i32.eqz
if
@@ -453,6 +465,8 @@
f64.const inf
call $~lib/object/Object.is<f64>
i32.const 0
i32.ne
i32.const 0
i32.eq
i32.eqz
if
@@ -466,6 +480,8 @@
f64.const nan:0x8000000000000
f64.const nan:0x8000000000000
call $~lib/object/Object.is<f64>
i32.const 0
i32.ne
i32.const 1
i32.eq
i32.eqz
@@ -480,6 +496,8 @@
f32.const 0
f32.const 0
call $~lib/object/Object.is<f32>
i32.const 0
i32.ne
i32.const 1
i32.eq
i32.eqz
@@ -495,6 +513,8 @@
f32.const -0
call $~lib/object/Object.is<f32>
i32.const 0
i32.ne
i32.const 0
i32.eq
i32.eqz
if
@@ -509,6 +529,8 @@
f32.const 0
call $~lib/object/Object.is<f32>
i32.const 0
i32.ne
i32.const 0
i32.eq
i32.eqz
if
@@ -523,6 +545,8 @@
f32.const 1
call $~lib/object/Object.is<f32>
i32.const 0
i32.ne
i32.const 0
i32.eq
i32.eqz
if
@@ -537,6 +561,8 @@
f32.const -1
call $~lib/object/Object.is<f32>
i32.const 0
i32.ne
i32.const 0
i32.eq
i32.eqz
if
@@ -550,6 +576,8 @@
f32.const 1
f32.const 1
call $~lib/object/Object.is<f32>
i32.const 0
i32.ne
i32.const 1
i32.eq
i32.eqz
@@ -564,6 +592,8 @@
f32.const inf
f32.const inf
call $~lib/object/Object.is<f32>
i32.const 0
i32.ne
i32.const 1
i32.eq
i32.eqz
@@ -580,6 +610,8 @@
f32.const inf
f32.neg
call $~lib/object/Object.is<f32>
i32.const 0
i32.ne
i32.const 1
i32.eq
i32.eqz
@@ -596,6 +628,8 @@
f32.neg
call $~lib/object/Object.is<f32>
i32.const 0
i32.ne
i32.const 0
i32.eq
i32.eqz
if
@@ -611,6 +645,8 @@
f32.const inf
call $~lib/object/Object.is<f32>
i32.const 0
i32.ne
i32.const 0
i32.eq
i32.eqz
if
@@ -625,6 +661,8 @@
f32.const nan:0x400000
call $~lib/object/Object.is<f32>
i32.const 0
i32.ne
i32.const 0
i32.eq
i32.eqz
if
@@ -639,6 +677,8 @@
f32.const inf
call $~lib/object/Object.is<f32>
i32.const 0
i32.ne
i32.const 0
i32.eq
i32.eqz
if
@@ -652,6 +692,8 @@
f32.const nan:0x400000
f32.const nan:0x400000
call $~lib/object/Object.is<f32>
i32.const 0
i32.ne
i32.const 1
i32.eq
i32.eqz
@@ -669,6 +711,8 @@
f64.mul
call $~lib/object/Object.is<f64>
i32.const 0
i32.ne
i32.const 0
i32.eq
i32.eqz
if
@@ -685,6 +729,8 @@
f64.mul
call $~lib/object/Object.is<f64>
i32.const 0
i32.ne
i32.const 0
i32.eq
i32.eqz
if
@@ -698,6 +744,8 @@
f64.const 0
f64.const 0
call $~lib/object/Object.is<f64>
i32.const 0
i32.ne
i32.const 1
i32.eq
i32.eqz