Мне нужно переписать устаревший код Java, выполняющий арифметические преобразования из Java в TypeScript/JavaScript. Проблема в том, что устаревший код использует тип int
Java (32-битный со знаком) и полагается на переполнения. Я почти получил то, что хотел, используя Int32Array в JavaScript, но у меня все еще есть разница, которую я не могу объяснить. Смотри ниже.
Джава:
int current = -1599751945;
int next = current * 0x08088405 + 1;
System.out.println("next = " + next);
Вывод: следующий = 374601940
Javascript:
const a = new Int32Array(4)
a[0] = -1599751945
a[1] = 0x08088405
a[2] = 1
a[3] = a[0]*a[1] + a[2]
console.info('a[3] = ' + a[3])
Выход: а[3] = 374601952
Может кто-нибудь объяснить разницу? И как я могу получить такой же результат в JavaScript? Я пробовал операции сдвига, принуждение с помощью | 0, методы преобразования и т. д., Но лучший результат - тот, что выше.
Я не могу дать окончательный ответ, почему именно вы получаете эти точные цифры; но учтите, что все числа в JS являются двойными.
Итак, в то время как current * 0x08088405
выполняется с использованием целочисленной арифметики в Java, a[0]*a[1]
выполняется с использованием двойной арифметики в JS, поэтому эти промежуточные результаты отличаются; и ограниченная точность двойного числа означает, что добавление 1 к этому фактически не меняет значение:
console.info(a[0]*a[1]) => -215607868985706270
console.info(a[0]*a[1] + a[2]) => = -215607868985706270
Сравните это с Java, где используется целочисленная арифметика:
int[] a = { -1599751945, 0x08088405, 1};
System.out.println(a[0]*a[1]) => 374601939
System.out.println(a[0]*a[1] + a[2]) => 374601940
Если мы заставим Java делать это в двойной арифметике:
double[] a = { -1599751945, 0x08088405, 1};
System.out.println(a[0]*a[1]); => -2.15607868985706272E17
System.out.println(a[0]*a[1] + a[2]); => -2.15607868985706272E17
Вы можете видеть, что это почти то же самое, но отличается младшей значащей цифрой:
-215607868985706270 // JS
-215607868985706272 // Java
Я не знаю, почему здесь такая разница.
Спасибо за подробное объяснение!
Числа с плавающей запятой поддерживают точность только до определенного количества цифр. JavaScript преобразует числа больше 32 бит в числа с плавающей запятой.
Java здесь "более правильный".
// -215607868985706285 is the 64 bit result of your multiplication
console.info(-215607868985706285); // will print -215607868985706285
См. Не работает ли математика с плавающей запятой? для общего обсуждения этой темы.
Используйте Math.imul() в JavaScript. Это должно дать правильный результат.
const a = new Int32Array(4)
a[0] = -1599751945
a[1] = 0x08088405
a[2] = 1
a[3] = Math.imul(a[0], a[1]) + a[2]
console.info('a[3] = ' + a[3])
Дополнительную информацию о том, почему можно найти здесь.
Связанный вопрос, который может помочь: stackoverflow.com/q/30678303/5133585