-
-
Notifications
You must be signed in to change notification settings - Fork 116
/
lemire.gr
249 lines (236 loc) · 9.03 KB
/
lemire.gr
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
// This module was based on Rust's dec2flt
// https://github.com/rust-lang/rust/blob/1cbc45942d5c0f6eb5d94e3b10762ba541958035/library/core/src/num/dec2flt/lemire.rs
// Rust's MIT license is provided below:
/*
* Permission is hereby granted, free of charge, to any
* person obtaining a copy of this software and associated
* documentation files (the "Software"), to deal in the
* Software without restriction, including without
* limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software
* is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice
* shall be included in all copies or substantial portions
* of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
* ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
* SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
* IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
@noPervasives
module Lemire
from "runtime/unsafe/wasmi32" include WasmI32
from "runtime/unsafe/wasmi64" include WasmI64
from "runtime/dataStructures" include DataStructures
use DataStructures.{ newInt32, newInt64, newFloat64 }
from "runtime/atof/table" include Table
use Table.{ get_POWERS5 }
from "runtime/atof/common" include Common
use Common.{
_SMALLEST_POWER_OF_FIVE,
_LARGEST_POWER_OF_FIVE,
_INFINITE_POWER,
_SMALLEST_POWER_OF_TEN,
_LARGEST_POWER_OF_TEN,
_MANTISSA_EXPLICIT_BITS_32,
_MANTISSA_EXPLICIT_BITS_64,
_MINIMUM_EXPONENT,
_MIN_EXPONENT_ROUND_TO_EVEN,
_MAX_EXPONENT_ROUND_TO_EVEN,
fullMultiplication,
power,
type BiasedFp,
fpZero,
fpInf,
fpErr,
}
primitive (&&) = "@and"
primitive (||) = "@or"
primitive (!) = "@not"
// From Rust:
// This will compute or rather approximate w * 5**q and return a pair of 64-bit words
// approximating the result, with the "high" part corresponding to the most significant
// bits and the low part corresponding to the least significant bits.
@unsafe
let computeProductApprox = (q: WasmI64, w: WasmI64, precision: WasmI64) => {
use WasmI64.{ (==), (&), gtU as (>), ltU as (<), (>=), (<=), (>>>), (-), (+) }
use WasmI32.{ (*) }
let mask = if (precision < 64N) {
0xFFFF_FFFF_FFFF_FFFFN >>> precision
} else {
0xFFFF_FFFF_FFFF_FFFFN
}
// From Rust:
// 5^q < 2^64, then the multiplication always provides an exact value.
// That means whenever we need to round ties to even, we always have
// an exact value.
let index = q - _SMALLEST_POWER_OF_FIVE
let n = 16n * WasmI32.wrapI64(index)
use WasmI32.{ (+) }
let lo5 = WasmI64.load(get_POWERS5(), n)
and hi5 = WasmI64.load(get_POWERS5(), n + 8n)
use WasmI64.{ (+) }
// From Rust:
// Only need one multiplication as long as there is 1 zero but
// in the explicit mantissa bits, +1 for the hidden bit, +1 to
// determine the rounding direction, +1 for if the computed
// product has a leading zero.
let (firstLo, firstHi) = fullMultiplication(w, lo5)
let mut firstLo = WasmI64.load(WasmI32.fromGrain(firstLo), 8n)
let mut firstHi = WasmI64.load(WasmI32.fromGrain(firstHi), 8n)
if ((firstHi & mask) == mask) {
// From Rust:
// Need to do a second multiplication to get better precision
// for the lower product. This will always be exact
// where q is < 55, since 5^55 < 2^128. If this wraps,
// then we need to need to round up the hi product.
let (_, secondHi) = fullMultiplication(w, hi5)
let secondHi = WasmI64.load(WasmI32.fromGrain(secondHi), 8n)
firstLo += secondHi
if (secondHi > firstLo) {
firstHi += 1N
}
}
(
WasmI32.toGrain(newInt64(firstLo)): Int64,
WasmI32.toGrain(newInt64(firstHi)): Int64,
)
}
// Returns the significant bits and a biased binary exponent that can be directly shifted into exponent bits:
// (u64, i32)
@unsafe
provide let computeFloat = (exponent: WasmI64, mantissa: WasmI64) => {
use WasmI64.{ (==), (<), (<=), (>), (>=), (<<), (>>), (&), (+), (>>>) }
let w = mantissa
let q = exponent
if (w == 0N || q < _SMALLEST_POWER_OF_TEN) {
fpZero()
} else if (q > _LARGEST_POWER_OF_TEN) {
fpInf()
} else {
let lz = WasmI64.clz(w)
let w = w << lz
let (lo, hi) = computeProductApprox(q, w, _MANTISSA_EXPLICIT_BITS_64 + 3N)
let lo = WasmI64.load(WasmI32.fromGrain(lo), 8n)
let hi = WasmI64.load(WasmI32.fromGrain(hi), 8n)
// From Rust:
// If we have failed to approximate w x 5^-q with our 128-bit value.
// Since the addition of 1 could lead to an overflow which could then
// round up over the half-way point, this can lead to improper rounding
// of a float.
//
// However, this can only occur if q ∈ [-27, 55]. The upper bound of q
// is 55 because 5^55 < 2^128, however, this can only happen if 5^q > 2^64,
// since otherwise the product can be represented in 64-bits, producing
// an exact result. For negative exponents, rounding-to-even can
// only occur if 5^-q < 2^64.
//
// For detailed explanations of rounding for negative exponents, see
// <https://arxiv.org/pdf/2101.11408.pdf#section.9.1>. For detailed
// explanations of rounding for positive exponents, see
// <https://arxiv.org/pdf/2101.11408.pdf#section.8>.
match (lo == 0xFFFF_FFFF_FFFF_FFFFN && !(q >= -27N && q <= 55N)) {
true => fpErr(),
false => {
let upperbit = WasmI32.wrapI64(hi >>> 63N)
let mut mantissa = hi >>> WasmI64.extendI32S({
use WasmI32.{ (+), (-) }
upperbit + 64n - _MANTISSA_EXPLICIT_BITS_32 - 3n
})
let mut power2 = {
use WasmI32.{ (+), (-) }
let q = WasmI32.wrapI64(q)
let lz = WasmI32.wrapI64(lz)
power(q) + upperbit - lz - _MINIMUM_EXPONENT
}
use WasmI32.{ (<=) }
if (power2 <= 0n) {
use WasmI32.{ (+), (*), (>=) }
// -power2 + 1 >= 64
if (-1n * power2 + 1n >= 64n) {
// Have more than 64 bits below the minimum exponent, must be 0.
fpZero()
} else {
use WasmI64.{ (>=) }
// Have a subnormal value.
mantissa = mantissa >> WasmI64.extendI32S(-1n * power2 + 1n)
use WasmI64.{ (+) }
mantissa += mantissa & 1N
mantissa = mantissa >> 1N
power2 = if (mantissa >= 1N << _MANTISSA_EXPLICIT_BITS_64) {
1n
} else {
0n
}
{
f: WasmI32.toGrain(newInt64(mantissa)): Int64,
e: WasmI32.toGrain(newInt32(power2)): Int32,
}
}
} else {
use WasmI64.{ (<=) }
// Need to handle rounding ties. Normally, we need to round up,
// but if we fall right in between and and we have an even basis, we
// need to round down.
//
// This will only occur if:
// 1. The lower 64 bits of the 128-bit representation is 0.
// IE, 5^q fits in single 64-bit word.
// 2. The least-significant bit prior to truncated mantissa is odd.
// 3. All the bits truncated when shifting to mantissa bits + 1 are 0.
//
// Or, we may fall between two floats: we are exactly halfway.
if (
WasmI64.leU(lo, 1N) &&
q >= _MIN_EXPONENT_ROUND_TO_EVEN &&
q <= _MAX_EXPONENT_ROUND_TO_EVEN &&
(mantissa & 3N) == 1N &&
mantissa << WasmI64.extendI32S({
use WasmI32.{ (+), (-) }
upperbit + 64n - _MANTISSA_EXPLICIT_BITS_32 - 3n
}) == hi
) {
use WasmI64.{ (^) }
// Zero the lowest bit, so we don't round up.
// mantissa &= !1N;
mantissa = mantissa & (1N ^ -1N)
}
// Round-to-even, then shift the significant digits into place.
mantissa += mantissa & 1N
use WasmI64.{ (>>>) }
mantissa = mantissa >>> 1N
if (WasmI64.geU(mantissa, 2N << _MANTISSA_EXPLICIT_BITS_64)) {
use WasmI32.{ (+) }
// Rounding up overflowed, so the carry bit is set. Set the
// mantissa to 1 (only the implicit, hidden bit is set) and
// increase the exponent.
mantissa = 1N << _MANTISSA_EXPLICIT_BITS_64
power2 += 1n
}
use WasmI32.{ (>=) }
use WasmI64.{ (^) }
// Zero out the hidden bit.
mantissa = mantissa & (1N << _MANTISSA_EXPLICIT_BITS_64 ^ -1N)
if (power2 >= _INFINITE_POWER) {
// Exponent is above largest normal value, must be infinite.
fpInf()
} else {
{
f: WasmI32.toGrain(newInt64(mantissa)): Int64,
e: WasmI32.toGrain(newInt32(power2)): Int32,
}
}
}
},
}
}
}