aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlon Zakai <alonzakai@gmail.com>2013-08-19 17:33:46 -0700
committerAlon Zakai <alonzakai@gmail.com>2013-08-19 18:35:36 -0700
commit83972554f5fce6b2205d28211e115195dbdf6523 (patch)
tree758dd72852df8bf6000f91743b1abef521bedc0b
parent50801f09f387731502e4f88358181f6300823360 (diff)
round doubles to i64s more carefully, especially small negatives; fixes #1539
-rw-r--r--src/parseTools.js28
-rw-r--r--tests/test_core.py77
2 files changed, 101 insertions, 4 deletions
diff --git a/src/parseTools.js b/src/parseTools.js
index 65e96264..046dac1b 100644
--- a/src/parseTools.js
+++ b/src/parseTools.js
@@ -727,15 +727,37 @@ function makeI64(low, high) {
// Splits a number (an integer in a double, possibly > 32 bits) into an USE_TYPED_ARRAYS == 2 i64 value.
// Will suffer from rounding. mergeI64 does the opposite.
function splitI64(value, floatConversion) {
- // We need to min here, since our input might be a double, and large values are rounded, so they can
+ // general idea:
+ //
+ // $1$0 = ~~$d >>> 0;
+ // $1$1 = Math_abs($d) >= 1 ? (
+ // $d > 0 ? Math.min(Math_floor(($d)/ 4294967296.0), 4294967295.0)
+ // : Math_ceil(Math.min(-4294967296.0, $d - $1$0)/ 4294967296.0)
+ // ) : 0;
+ //
+ // We need to min on positive values here, since our input might be a double, and large values are rounded, so they can
// be slightly higher than expected. And if we get 4294967296, that will turn into a 0 if put into a
// HEAP32 or |0'd, etc.
+ //
+ // For negatives, we need to ensure a -1 if the value is overall negative, even if not significant negative component
+
var lowInput = legalizedI64s ? value : 'VALUE';
if (floatConversion && ASM_JS) lowInput = asmFloatToInt(lowInput);
+ var low = lowInput + '>>>0';
+ var high = makeInlineCalculation(
+ asmCoercion('Math.abs(VALUE)', 'double') + ' >= ' + asmEnsureFloat('1', 'double') + ' ? ' +
+ '(VALUE > ' + asmEnsureFloat('0', 'double') + ' ? ' +
+ asmCoercion('Math.min(' + asmCoercion('Math.floor((VALUE)/' + asmEnsureFloat(4294967296, 'float') + ')', 'double') + ', ' + asmEnsureFloat(4294967295, 'float') + ')', 'i32') + '>>>0' +
+ ' : ' + asmFloatToInt(asmCoercion('Math.ceil((VALUE - +((' + asmFloatToInt('VALUE') + ')>>>0))/' + asmEnsureFloat(4294967296, 'float') + ')', 'double')) + '>>>0' +
+ ')' +
+ ' : 0',
+ value,
+ 'tempDouble'
+ );
if (legalizedI64s) {
- return [lowInput + '>>>0', asmCoercion('Math.min(' + asmCoercion('Math.floor((' + value + ')/' + asmEnsureFloat(4294967296, 'float') + ')', 'double') + ', ' + asmEnsureFloat(4294967295, 'float') + ')', 'i32') + '>>>0'];
+ return [low, high];
} else {
- return makeInlineCalculation(makeI64(lowInput + '>>>0', asmCoercion('Math.min(' + asmCoercion('Math.floor(VALUE/' + asmEnsureFloat(4294967296, 'float') + ')', 'double') + ', ' + asmEnsureFloat(4294967295, 'float') + ')', 'i32') + '>>>0'), value, 'tempBigIntP');
+ return makeI64(low, high);
}
}
function mergeI64(value, unsigned) {
diff --git a/tests/test_core.py b/tests/test_core.py
index 35f5f0f9..f3ddc4f4 100644
--- a/tests/test_core.py
+++ b/tests/test_core.py
@@ -808,6 +808,80 @@ nada
'''
self.do_run(src, ',0,,2,C!,0,C!,0,,65535,C!,0,')
+ def test_double_i64_conversion(self):
+ if Settings.USE_TYPED_ARRAYS != 2: return self.skip('needs ta2')
+
+ src = r'''
+ #include <cassert>
+ #include <inttypes.h>
+ #include <stdio.h>
+
+ __attribute((noinline)) bool eq(double d, int64_t i) {
+ int64_t i2 = (int64_t)d;
+ if (i != i2) {
+ printf("%.20g converted to int64 returns %lld, not %lld as expected!\n", d, i2, i);
+ }
+ return i == i2;
+ }
+
+ int main() {
+ assert(eq(0.0, 0));
+ assert(eq(-0.0, 0));
+ assert(eq(0.1, 0));
+ assert(eq(-0.1, 0));
+ assert(eq(0.6, 0));
+ assert(eq(-0.6, 0));
+ assert(eq(1.0, 1));
+ assert(eq(-1.0, -1));
+ assert(eq(1.1, 1));
+ assert(eq(-1.1, -1));
+ assert(eq(1.6, 1));
+ assert(eq(-1.6, -1));
+ assert(eq(4294967295.0, 4294967295LL));
+ assert(eq(4294967295.5, 4294967295LL));
+ assert(eq(4294967296.0, 4294967296LL));
+ assert(eq(4294967296.5, 4294967296LL));
+ assert(eq(14294967295.0, 14294967295LL));
+ assert(eq(14294967295.5, 14294967295LL));
+ assert(eq(14294967296.0, 14294967296LL));
+ assert(eq(14294967296.5, 14294967296LL));
+ assert(eq(-4294967295.0, -4294967295LL));
+ assert(eq(-4294967295.5, -4294967295LL));
+ assert(eq(-4294967296.0, -4294967296LL));
+ assert(eq(-4294967296.5, -4294967296LL));
+ assert(eq(-14294967295.0, -14294967295LL));
+ assert(eq(-14294967295.5, -14294967295LL));
+ assert(eq(-14294967296.0, -14294967296LL));
+ assert(eq(-14294967296.5, -14294967296LL));
+
+ assert(eq(4294967295.3, 4294967295LL));
+ assert(eq(4294967296.3, 4294967296LL));
+ assert(eq(14294967295.3, 14294967295LL));
+ assert(eq(14294967296.3, 14294967296LL));
+ assert(eq(-4294967295.3, -4294967295LL));
+ assert(eq(-4294967296.3, -4294967296LL));
+ assert(eq(-14294967295.3, -14294967295LL));
+ assert(eq(-14294967296.3, -14294967296LL));
+
+ assert(eq(4294967295.8, 4294967295LL));
+ assert(eq(4294967296.8, 4294967296LL));
+ assert(eq(14294967295.8, 14294967295LL));
+ assert(eq(14294967296.8, 14294967296LL));
+ assert(eq(-4294967295.8, -4294967295LL));
+ assert(eq(-4294967296.8, -4294967296LL));
+ assert(eq(-14294967295.8, -14294967295LL));
+ assert(eq(-14294967296.8, -14294967296LL));
+
+ // The following number is the largest double such that all integers smaller than this can exactly be represented in a double.
+ assert(eq(9007199254740992.0, 9007199254740992LL /* == 2^53 */));
+ assert(eq(-9007199254740992.0, -9007199254740992LL /* == -2^53 */));
+
+ printf("OK!\n");
+ return 0;
+ }
+ '''
+ self.do_run(src, 'OK!\n');
+
def test_negative_zero(self):
src = r'''
#include <stdio.h>
@@ -10066,4 +10140,5 @@ for compiler, quantum, embetter, typed_arrays in [
)
locals()[fullname] = make_run(fullname, fullname, compiler, embetter, quantum, typed_arrays)
-del T # T is just a shape for the specific subclasses, we don't test it itself \ No newline at end of file
+del T # T is just a shape for the specific subclasses, we don't test it itself
+