Funkcja JavaScript przepisana w Javie daje różne wyniki

9

Jest taka funkcja Javascript, którą próbuję przepisać w Javie:

function normalizeHash(encondindRound2) {
    if (encondindRound2 < 0) {
        encondindRound2 = (encondindRound2 & 0x7fffffff) + 0x80000000;
    }
    return encondindRound2 % 1E6;
}

Moja adaptacja Java:

public long normalizeHash(long encondindRound2) {
        if (encondindRound2 < 0) {
            encondindRound2 = (((int) encondindRound2) & 0x7fffffff) + 0x80000000;
        }
        return (((int) encondindRound2) % 1_000_000);
    }

Kiedy przechodzę -1954896768, wersja Javascript wraca 70528, a Java wraca -896768. Nie jestem pewien dlaczego. Różnica wydaje się zaczynać w warunku if: w funkcji JavaScript po if encodingRound2 = 2340070528, podczas gdy w Javie:encodingRound2 = -1954896768 .

Zrobiłem te repliki, aby pokazać to online:

JavaScript : https://repl.it/repls/NumbGuiltyHack

Java : https://repl.it/repls/ClumsyQualifiedProblem

EDYCJA : Zmiana funkcji Java na to

public long normalizeHash(long encondindRound2) {
        if (encondindRound2 < 0) {
            encondindRound2 = (encondindRound2 & 0x7fffffff) + 0x80000000;
        }
        return (encondindRound2 % 1_000_000);
    }

wydaje się nie wpływać na wynik - nadal jest -896768

parsecer
źródło
1
Dlaczego przesyłasz encondindRound2do intkodu Java? Ponieważ jest zdefiniowany jako long, potencjalnie stracisz precyzję, jeśli rzucisz go na węższy typ.
Jordan
1
@Jordan, ponieważ - wewnątrz if - wykonywana jest na nim operacja bitowa. JavaScript, mimo że zapisuje liczby jako liczby zmiennoprzecinkowe w wersji 64-bitowej, podczas wykonywania bitów konwertuje liczby na 32-bitowe liczby całkowite. Miałem ten problem z innym fragmentem kodu już wcześniej, gdy bitowe z Javą longdawało różne wyniki, z powodu przepełnienia.
parsecer
Usunięcie (int)rzutowania z returnlinii nie zmienia wyniku Java, pozostaje-896768
parsecer
4
Ach, znalazłem problem. Kiedy się ... + 0x80000000przyłączysz, Java konwertuje wartość na liczbę całkowitą, ponieważ 0x80000000uważa się ją za literał int. Zmień ten numer na 0x80000000L.
Jordan
1
@Jordan Wow, jesteś czarodziejem! Zadziałało! Proszę wysłać odpowiedź i będę ją zaakceptować
parsecer

Odpowiedzi:

9

W Javie 0x80000000 jest poza zakresem 32-bitowej int, więc zawija się do -2147483648.

W JavaScript, 0x80000000 mieści się w zakresie podwójnego 64-bitowego, więc pozostaje 2147483648.

Oczywiście dodawanie -2147483648a dodawanie2147483648 powoduje bardzo dużą rozbieżność.

Możesz użyć long0x80000000L w Javie lub zmusić swój numer JS do 32-bitowej int (0x80000000|0), w zależności od tego, co chcesz.

ten inny facet
źródło
2

Spróbuj tego. Musisz podać długie wartości podczas konwersji.

    public static long normalizeHash(long encondindRound2) {
        if (encondindRound2 < 0) {
            encondindRound2 =  (encondindRound2 & 0x7fffffffL) + 0x80000000L;
        }

        return  (encondindRound2 % 1_000_000);
    }

Ale jest jeszcze jedna kwestia, o której powinieneś wiedzieć. JavaScript traktuje %jak operator modulo, gdzie Java traktuje go jako prosty operator reszty. Sprawdź ten post tutaj, aby uzyskać więcej informacji.

WJS
źródło