При внесении нескольких больших изменений рефакторинга , которые не изменяли какой-либо арифметики, мне удалось каким-то образом изменить вывод моей программы (системы моделирования на основе агентов). Различные цифры на выходе теперь отключены минимальными суммами. Экзамен показывает, что эти номера отключены на 1 бит в младшем значении.
Например, 24.198110084326416 станет 24.19811008432642. Отображение с плавающей запятой каждого номера:
24.198110084326416 = 0 10000000011 1000001100101011011101010111101011010011000010010100
24.19811008432642 = 0 10000000011 1000001100101011011101010111101011010011000010010101
В котором мы замечаем, что младший значащий бит отличается.
Мой вопрос в том, как я мог бы ввести это изменение, если бы не менял какой-либо арифметики? Это изменение включало упрощение объекта путем удаления наследования (его суперкласс был вздут с помощью методов, которые не были применимы к этому классу).
Я отмечаю, что вывод (отображающий значения определенных переменных при каждом тике симуляции) иногда выключается, а затем для другого тика, числа ожидаются, только для того, чтобы снова отключиться для следующего тика (например, на одном агенте его значения показывают эту проблему на отметках 57 - 83, но, как и ожидалось, для тиков 84 и 85, только для того, чтобы снова отключиться для отметки 86).
Я знаю, что мы не должны сравнивать числа с плавающей запятой напрямую. Эти ошибки были замечены, когда тест интеграции, который просто сравнивал выходной файл с ожидаемым выходом, не удался. Я мог бы (и, возможно, должен) исправить тест, чтобы проанализировать файлы и сравнить парсированные парные с некоторым epsilon, но мне все еще интересно узнать, почему этот вопрос может быть представлен.
EDIT:
Минимальная разница в изменении, которая ввела проблему:
diff --git a/src/main/java/modelClasses/GridSquare.java b/src/main/java/modelClasses/GridSquare.java
index 4c10760..80276bd 100644
--- a/src/main/java/modelClasses/GridSquare.java
+++ b/src/main/java/modelClasses/GridSquare.java
@@ -63,7 +63,7 @@ public class GridSquare extends VariableLevel
public void addHousehold(Household hh)
{
assert household == null;
- subAgents.add(hh);
+ neighborhood.getHouseholdList().add(hh);
household = hh;
}
@@ -73,7 +73,7 @@ public class GridSquare extends VariableLevel
public void removeHousehold()
{
assert household != null;
- subAgents.remove(household);
+ neighborhood.getHouseholdList().remove(household);
household = null;
}
diff --git a/src/main/java/modelClasses/Neighborhood.java b/src/main/java/modelClasses/Neighborhood.java
index 834a321..8470035 100644
--- a/src/main/java/modelClasses/Neighborhood.java
+++ b/src/main/java/modelClasses/Neighborhood.java
@@ -166,9 +166,14 @@ public class Neighborhood extends VariableLevel
World world;
/**
+ * List of all grid squares within the neighborhood.
+ */
+ ArrayList<VariableLevel> gridSquareList = new ArrayList<>();
+
+ /**
* A list of empty grid squares within the neighborhood
*/
- ArrayList<GridSquare> emptyGridSquareList;
+ ArrayList<GridSquare> emptyGridSquareList = new ArrayList<>();
/**
* The neighborhood grid square bounds
@@ -836,7 +841,7 @@ public class Neighborhood extends VariableLevel
*/
public GridSquare getGridSquare(int i)
{
- return (GridSquare) (subAgents.get(i));
+ return (GridSquare) gridSquareList.get(i);
}
/**
@@ -865,7 +870,7 @@ public class Neighborhood extends VariableLevel
@Override
public ArrayList<VariableLevel> getGridSquareList()
{
- return subAgents;
+ return gridSquareList;
}
/**
@@ -874,12 +879,7 @@ public class Neighborhood extends VariableLevel
@Override
public ArrayList<VariableLevel> getHouseholdList()
{
- ArrayList<VariableLevel> list = new ArrayList<VariableLevel>();
- for (int i = 0; i < subAgents.size(); i++)
- {
- list.addAll(subAgents.get(i).getHouseholdList());
- }
- return list;
+ return subAgents;
}
К сожалению, я не могу создать небольшой компилируемый пример из-за того, что мне не удается воспроизвести это поведение вне программы и не разрезать эту очень большую и запутанную программу до размера.
Что касается операций с плавающей запятой, нет ничего особенно захватывающего. Тонна добавления, умножения, натуральных логарифмов и степеней (почти всегда с базой e). Последние два сделаны со стандартной библиотекой. Случайные числа используются во всей программе и генерируются с Random
классом, включенным в используемую структуру (Repast).
Большинство чисел находятся в диапазоне от 1e-3 до 1e5. Там почти нет очень больших или очень маленьких чисел. Бесконечность и NaN используются во многих местах.
Являясь системой моделирования на основе агентов, многие формулы повторяются для имитации появления. Порядок оценки очень важен (так как многие переменные зависят от того, какие другие оцениваются в первую очередь, например, для расчета ИМТ, нам необходимо сначала рассчитать диету и сердечно-сосудистую систему). Предыдущие значения переменных также очень важны во многих вычислениях (так что эта проблема может быть внедрена где-то в начале программы и переноситься по всему остальному).