2013-07-07 4 views
6

ОБНОВЛЕНИЕ: Спасибо, Кевин, я имел в виду < = в названии, а не <.Для double f, float g, Как найти наибольший int i, так что i * g <= f

Это правильно? Это лучшее, что я сумел после неловкого количества времени на проблеме:

public int candidate_answer(double f, float g) { 
    int test = (int)Math.floor(f/g); 
    if ((test + 1) * g <= f) { 
     test++; 
    } 
    return test; 
} 

фон:

Приложения простой игра, что я перешла на собственность от предыдущего программиста , Любопытно, что он решил смешивать поплавки и удваивать бессмысленно, как в переменных-членах, так и в переменных, поэтому есть много ненужного неявного и явного кастинга, идущего вверх и вниз.

Координаты игрока в двойном x, y (игрок считается точкой). Существует плавающий TILE_SIZE, а в мире есть некоторое количество строк и столбцов, количество плиток, а также некоторая общая обработка вне границ. Предполагая, что координата (x, y) находится в границах, я пытаюсь выяснить, в каком элементе находится пользователь, на основе x (для получения столбца) или y (для получения строки). Это похоже, программист 101.

WLOG, Сначала я просто делал col = (int)(x/TILE_SIZE). Но, как я обнаружил, например, .5/.1f < 5 и, следовательно, (int)(.5/.1f) == 4, неправильный ответ. Это привело к вышесказанному утверждению и постановке проблемы.

Затем я обнаружил, что, например, (int)(-9.999999747378752E-5/.1f) == 0, что привело меня сначала позвонить Math.floor.

Но теперь я не уверен, какие другие ошибки скрываются в этом подходе или как лучший подход.

(Возможно, это не так важно, если пользователь находится на пороге присутствия в одной строке или другой, и мы случайно округлились до неправильного, но реальная проблема находится в коде, где мы см. неожиданное изменение знака (+, -, 0). Например, некоторые из кода предполагают, что если пользователь находится на плитке в точке (r, c), то это точка фактически содержится в этой геометрии плитки. Когда это не так, мы получаем такие вещи, как отрицательные расстояния, когда ожидаются только неотрицательные, и т. д., и это приводит к тому, что сигналы срабатывают, а в то время как петли ломаются и т. д.)

+0

'i * g Kevin

+0

<= и обновлено спасибо – jdowdell

ответ

3

Я думаю, вы должны попытаться устранить проблему с корнем (. 1f слишком далеко от .1), т.е. поверните TILE_SIZE в double или используйте поплавки последовательно повсюду. В противном случае вы можете столкнуться с подобными проблемами, вызванными минимальными ошибками округления по всему коду. Функция candid_answer - это взломать проблему, которая не должна существовать в первую очередь.

P.S.

Для этой конкретной проблемы вы можете просто найти формулу, которая является более надежной, то есть не чувствительна к минимальным ошибкам округления только в одном направлении. Попробуйте поставить игрока в центре его поля, а не на краю плитки, где он может легко впасть в ошибки округления:

col = (int)((x + MINIMAL_STEP_SIZE/2.0)/TILE_SIZE) 

(В идеале, MINIMAL_STEP_SIZE/2 часть уже будет частью х вместо того, чтобы добавлено здесь)

+1

Я согласен с исправлением проблемы с корнем, но я думаю, что лучший способ сделать это чтобы переключиться на интегральное представление с фиксированной запятой. С TILE_SIZE 256 и всеми координатами 32-разрядных целых чисел, у вас есть много разрешений в каждой плите, а площадь карты - 16 миллионов плиток. – NovaDenizen

+0

Согласовано, но это может быть гораздо более крупным рефакторингом, а фиксированная точка имеет свой собственный набор проблем (например, забота о переполнении в умножениях, может быть сложнее отлаживать). –

1

Использование дубинок на весь путь, кажется, путь. Однако это не решает проблему с корнем: использование арифметики с плавающей точкой всегда связано с ошибками округления.

При использовании арифметики с плавающей точкой, это всегда хорошо работать с Эпсилон (е) точность:

Если вы хотите проверить, если х равно у (где одна переменная типа с плавающей точкой) используйте следующую закономерность:

if(x-y < e){ 
    ... 
} 

Эпсилон должен быть определен (глобально или по вашему мнению, для определенной области) на основе ваших требований. Например:

e = 0.00001d; 
+0

Бросьте эпсилон, и теперь у вас есть две проблемы вместо одного: что такое правильный универсальный эпсилон? И вы не показываете, как это может помочь в реальной исходной проблеме. Если вы это сделаете, вы можете просто использовать числа с фиксированной точкой вместо этого, потому что в основном он убивает аспект с плавающей запятой чисел с плавающей запятой. Учитывайте значение 0.000001 и 0.000002: разница ниже вашего эпсилона, но одно число вдвое больше. Я думаю, что здесь основная проблема заключается в том, чтобы позиционировать игрока с размером точек прямо на краю своего поля, а не на середине его, что делает все склонным к ошибкам округления. –

0

Если f и g оба положительны (они кажутся), ваш метод может быть сведена к одному, простой расчет:

public static int candidate_answer(double f, float g) { 
    return (int)(g/f); 
} 

Это правильно, потому что если

i*f <= g 

затем путем деления обе стороны от f дает

i <= g/f 

Кастинг для int автоматически дает вам «наибольшее значение int, меньшее или равное результату», потому что при отбрасывании всех нецелых частей они отбрасываются - это похоже на выполнение вызова floor().


Если f или g могут быть отрицательными, этот подход будет по-прежнему работать, но дело со знаком должны быть добавлены (не много кода, но я сомневаюсь, что это требуется с учетом их пути возникает вопрос

+0

Вы вообще прочитали вопрос? «Сначала я просто делал col = (int) (x/TILE_SIZE). Но, как я обнаружил, например, .5/.1f <5 и, следовательно, (int) (.5/.1f) == 4, неправильный ответ. Это привело к вышесказанному утверждению и постановке проблемы ». –

Смежные вопросы