Javascript - эта функция шума будет работать? - PullRequest
0 голосов
/ 16 апреля 2019

У меня есть следующая детерминированная шумовая функция, которую я использовал некоторое время в генераторе ландшафта C # и C ++:

float GridNoise(int x, int z, int seed)
{
    int n = (1619*x + 31337*z + 1013*seed) & 0x7fffffff;
    n = (n >> 13) ^ n;

    return 1 - ((n*(n*n*60493 + 19990303) + 1376312589) & 0x7fffffff)/(float)1073741824;
}

Возвращает «случайное» число с плавающей запятой между 1 и -1 для любого целочисленного значения координат x / z, которое я ввожу (плюс есть начальное число, поэтому я могу генерировать различные ландшафты). Я пытался реализовать ту же функцию в Javascript, но результаты не такие, как ожидалось. Для небольших значений это нормально, но поскольку я использую большие значения (порядка ~ 10000), результаты становятся все менее и менее случайными, и в итоге все возвращается 1. 1. 1004 *

Вы можете видеть, что он работает правильно в C # здесь , и неправильные результаты JS для того же ввода здесь .

Я подозреваю, что это связано с тем, что переменные JS не являются строгими целыми числами, но кто-нибудь может пролить больше света? У кого-нибудь есть такая же простая детерминированная функция, которую я мог бы использовать в JS, если она не работает?

Ответы [ 4 ]

1 голос
/ 16 апреля 2019

Основная проблема заключается в том, что в javascript нет целых чисел - поэтому все математические функции выполняются с использованием Number (52-битная точность с плавающей точкой)

В c #, если вы используете long, то любые переполнения просто отбрасываются

В javascript вам нужно справиться с этим самостоятельно

В браузеры приходит числовой формат, который поможет, но он еще не здесь - BigInt ... он в chrome / opera и за флагом в Firefox (рабочий стол, а не Android)

(нет слов в Edge (все равно нет) или Safari (новый IE) - и, конечно, IE никогда их не получит)

Лучшее, что я могу придумать, используя BigInt, это

function gridNoise(x, z, seed) {
    var n = (1619 * x + 31337 * z + 1013 * seed) & 0x7fffffff;
    n = BigInt((n >> 13) ^ n);
    n = n * (n * n * 60493n + 19990303n) + 1376312589n;
    n = parseInt(n.toString(2).slice(-31), 2);
    return 1 - n / 1073741824;
}

function test() {
    for (var i = 10000; i < 11000; i++) {
        console.log(gridNoise(0, 0, i));
    }
}
test();

Примечание: 60493n - это BigInt обозначение

Есть промежуточные библиотеки, которые вы могли бы использовать тем временем - https://github.com/peterolson/BigInteger.js

Следующее не работает и никогда не будет ... потому что 32-битный х 32-битный == 64-битный ... так что вы уже потеряете биты

Я неправильно прочитал код, и хотя n был только 19 бит (из-за >> 13)

Если вы ограничите результат n * n * 60493 32-битным ((на самом деле, я сделал это 31-битным ... так что ... в любом случае, похоже, что он работает хорошо

)
function gridNoise(x, z, seed) {
  var n = (1619 * x + 31337 * z + 1013 * seed) & 0x7fffffff;
  n = (n >> 13) ^ n;

  return 1 - ((n * (n * n * 60493 & 0x7fffffff + 19990303) + 1376312589) & 0x7fffffff) / 1073741824;
}

это тоже работает

return 1 - ((n*(n*n*60493 | 0 + 19990303)  + 1376312589) & 0x7fffffff)/1073741824;

Это ограничивает промежуточный результат 32 битами, которые могут быть или не быть "точными"

Возможно, вам придется поэкспериментировать с ним, если вы хотите скопировать именно то, что производит c #

0 голосов
/ 16 апреля 2019

Чтобы понять, что здесь происходит, нужно изучить тип JavaScripts number.Это в основном 53-битное целое число, которое сдвигается влево / вправо с использованием другого 11-битного целого числа, в результате чего получается 64-битное число.Поэтому, если у вас есть вычисление, которое приведет к получению 54-битного целого числа, оно просто берет верхние 53 биты и сдвигает их влево на 1. Теперь, если вы выполняете битовую математику для чисел, потребуется младшие 32 бита .Поэтому, если целое число больше 84 бит, выполнение побитового сдвига на нем всегда будет иметь значение 0. Поэтому числа, превышающие 32 бита, будут стремиться к 0 в JS при выполнении побитовых операций, в то время как C # всегда принимает младшие 32 бита, и поэтому результат будетс точностью до 32 бит (но большее число не может быть представлено).

  (2 + 2 ** 53) & (2 + 2 ** 53)  // 2
  (2 + 2 ** 54) & (2 + 2 ** 54) // 0
0 голосов
/ 16 апреля 2019

Изменить (извините за плохой предыдущий ответ): Как уже говорилось ранее, проблема связана также с вашими значениями, которые превышают размер JS Number.Если у вас есть код, работающий на C #, возможно, было бы целесообразно перенести эту функциональность в серверную часть ASP.NET, которая будет обрабатывать вычисления для пересылки результата через какой-то API

.
0 голосов
/ 16 апреля 2019

Боюсь, ваш код превышает максимально допустимый размер для целых чисел. Как только это происходит, он возвращает 1, потому что вычисление ((n*(n*n*60493 + 19990303) + 1376312589) & 0x7fffffff)/1073741824 всегда будет 0 - таким образом, 1 - 0 = 1

...