2013-11-24 16 views
5

Nie wiedziałem, czy to błąd w samej Lua, czy też robiłem coś nie tak. Nigdzie nie mogłem nic znaleźć. Używam Lua dla Windows (Lua 5.1.4):"interwał jest pusty", Lua math.random nie działa dla dużych liczb?

>return math.random(0, 1000000000) 
1251258 

Zwraca losową liczbę całkowitą z zakresu od 0 do 10000000000, zgodnie z oczekiwaniami. To wydaje się działać dla wszystkich innych wartości. Ale jeśli dodaję pojedynczy 0:

>return math.random(0, 10000000000) 
stdin:1: bad argument #2 to 'random' (interval is empty) 

Każda liczba wyższa niż ta sama.

Próbowałem dowiedzieć się dokładnie, jak duża liczba musi być przyczyną tego i znalazłem coś jeszcze dziwniejsze:

>return math.random(0, 2147483647) 
-75617745 

Jeśli wartość wynosi 2147483647 następnie daje mi liczb ujemnych. Każda wyższa wartość i powoduje błąd. Wszelkie niższe niż to i działa dobrze.

To 0b1111111111111111111111111111111 w systemie binarnym, 31 cyfr binarnych dokładnie. Nie jestem pewien, co to znaczy.

Odpowiedz

7

To nieoczekiwane zachowanie (bug?) Jest spowodowane jak math.random traktuje argumenty wejściowe przekazywane w Lua 5.1. Od lmathlib.c:

case 2: { /* lower and upper limits */ 
    int l = luaL_checkint(L, 1); 
    int u = luaL_checkint(L, 2); 
    luaL_argcheck(L, l<=u, 2, "interval is empty"); 
    lua_pushnumber(L, floor(r*(u-l+1))+l); /* int between `l' and `u' */ 
    break; 
} 

Jak wiadomo w C, standard int może reprezentować Wartości -2,147,483,648 do 2,147,483,647. Dodawanie +1 do 2,147,483,647, podobnie jak w przypadku użytkowej, będzie przepełnienie i owinąć wokół wartość dając -2,147,483,648. Wynik końcowy jest ujemny, ponieważ mnożymy liczbę dodatnią liczbą ujemną.

Co więcej, wszystko powyżej 2,147,483,647 zawiedzie luaL_argcheck z powodu zawijania przelewu.

Istnieje kilka sposobów rozwiązania tego problemu:

  • upgrade do Lua 5.2. Od tego czasu poprawiono ten problem, traktując argumenty wejściowe jako lua_Number.
  • Przełącz na LuaJIT które nie mają tego problemu przepełnienia całkowitą.
  • patch źródłem Lua 5,1 się z poprawką i rekompilacji.
  • Modyfikowanie losowe asortyment tak, aby nie przepełnić.
4

Jeśli potrzebujesz zasięg, który jest większy niż to, co przypadkowe Obsługuje funkcję (32-bitowe liczby całkowite lub podpisane 2^31 ze względu na bit znaku, ponieważ Math.random jest na poziomie C), ale mniejszy niż zakres Typ liczby "Lua" (oparty na What is the maximum value of a number in Lua?, 2^52, a może nawet 2^53), możesz spróbować wygenerować dwie liczby losowe: skaluj pierwszy do żądanego zakresu; dodaj drugi, aby "wypełnić lukę". Na przykład, powiedzmy, że chcesz zakres od 0 do 2^36. Największy z math.random to 2^31.Więc można zrobić:

-- 2^36 = 2^31 * 2^5 so 
scale = 2^5 
baseRand = scale * math.random(0, 2^31) 
-- baseRand is now between 0 and 2^36 but there are gaps of 2^5 in the set 
-- of possible values; fill the gaps with second random number: 
fillGap = math.random(0, 2^5) 
randNum = baseRand + fillGap 

to będzie działać tak długo, jak pożądany zakres jest mniejszy niż maksimum w Lua tłumacza numerów Lua, który jest konfigurowany parametr czas kompilacji, ale jeśli używasz akcji budować to 2^52 , bardzo duża liczba (choć nie tak duża jak największa długa liczba całkowita, 2^63).

Należy również zauważyć, że największą dodatnią N-bitową liczbą całkowitą jest 2^N-1 (nie 2^N), ale powyższą technikę można zastosować do dowolnego zakresu, można na przykład uzyskać skalę = 10^6, a następnie randNum = 10^6 * math.random (0, 10^8) + math.random (0, 10^6).