- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
在进行较大的重构更改时,未修改任何类型的算法,我设法以某种方式更改了我的程序(基于代理的模拟系统)的输出。输出中的各种数字现在相差极小。检查表明,这些数字的最低有效位相差 1 位。
例如,24.198110084326416 将变为 24.19811008432642。每个数字的浮点表示为:
24.198110084326416 = 0 10000000011 1000001100101011011101010111101011010011000010010100
24.19811008432642 = 0 10000000011 1000001100101011011101010111101011010011000010010101
我们注意到最低有效位不同。
我的问题是,当我没有修改任何类型的算术时,我怎么会引入这种变化?更改涉及通过删除继承来简化对象(它的父类(super class)因不适用于此类的方法而变得臃肿)。
我注意到输出(在模拟的每个刻度显示某些变量的值)有时会关闭,然后对于另一个刻度,数字符合预期,只是在下一个刻度再次关闭(例如,在一个智能体上,它的值在第 57 - 83 个节拍上表现出这个问题,但在第 84 个和第 85 个节拍上符合预期,只是在第 86 个节拍上再次关闭)。
我知道我们不应该直接比较 float 。当仅将输出文件与预期输出进行比较的集成测试失败时,就会注意到这些错误。我可以(也许应该)修复测试以解析文件并将解析的 double 值与一些 epsilon 进行比较,但我仍然很好奇为什么会引入此问题。
编辑:
引入问题的最小变化差异:
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's 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
class 生成。包含在正在使用的框架中 (Repast)。
大多数数字都在 1e-3 到 1e5 的范围内。几乎没有非常大或非常小的数字。 Infinity 和 NaN 被用在很多地方。
作为基于代理的模拟系统,许多公式被重复应用于模拟出现。评估的顺序非常重要(因为许多变量取决于首先评估的其他变量——例如,要计算 BMI,我们需要首先计算饮食和心脏状况)。变量的先前值在许多计算中也非常重要(因此这个问题可以在程序的早期某处引入,并贯穿整个程序的其余部分)。
最佳答案
以下是浮点表达式求值可能不同的几种方式:
(1) 浮点处理器具有“当前舍入模式”,这可能导致结果在最低有效位上有所不同。您可以进行调用,获取或设置当前值:向零舍入、向 -∞ 或向 +∞ 舍入。
(2) 听起来strictfp 与C 中的FLT_EVAL_METHOD 相关,它指定了中间计算中使用的精度。有时新版本的编译器会使用与旧版本不同的方法(我被那个版本咬了)。 {0,1,2} 分别对应于 {single,double,extended} 精度,除非被更高精度的操作数覆盖。
(3) 就像不同的编译器可以有不同的默认浮点计算方法一样,不同的机器可以使用不同的浮点计算方法。
(4) 单精度 IEEE 浮点运算是定义明确、可重复且与机器无关的。 double 也是如此。我已经(非常小心地)编写了跨平台浮点测试,它使用 SHA-1 哈希来检查计算的位准确性!但是,FLT_EVAL_METHOD=2时,中间计算使用扩展精度,使用64位、80位或128位浮点运算实现,因此很难获得跨平台和跨编译器的可重复性如果在中间计算中使用扩展精度。
(5) 浮点运算是非关联的,即
(A + B) + C ≠ A + (B + C)
因此不允许编译器对 float 的计算重新排序。
(6) 操作顺序很重要。以最大可能的精度计算大量数字之和的算法是按递增的数量级对它们求和。另一方面,如果两个数字的大小差异足够大
B < (A * epsilon)
然后将它们相加是一个空操作:
A + B = A
关于java - 什么会导致 float 在没有算术变化的情况下突然偏离 1 位,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25047290/
我是 Java 新手,这是我的代码, if( a.name == b.name && a.displayname == b.displayname && a.linknam
在下面的场景中,我有一个 bool 值。根据结果,我调用完全相同的函数,唯一的区别是参数的数量。 var myBoolean = ... if (myBoolean) { retrieve
我是一名研究 C++ 的 C 开发人员: 我是否正确理解如果我抛出异常然后堆栈将展开直到找到第一个异常处理程序?是否可以在不展开的情况下在任何 throw 上打开调试器(即不离开声明它的范围或任何更高
在修复庞大代码库中的错误时,我观察到一个奇怪的情况,其中引用的动态类型从原始 Derived 类型更改为 Base 类型!我提供了最少的代码来解释问题: struct Base { // some
我正在尝试用 C# 扩展给定的代码,但由于缺乏编程经验,我有点陷入困境。 使用 Visual Studio 社区,我尝试通过控制台读出 CPU 核心温度。该代码使用开关/外壳来查找传感器的特定名称(即
这可能是一个哲学问题。 假设您正在向页面发出 AJAX 请求(这是使用 Prototype): new Ajax.Request('target.asp', { method:"post", pa
我有以下 HTML 代码,我无法在所有浏览器中正常工作: 我试图在移动到
我对 Swift 很陌生。我如何从 addPin 函数中检索注释并能够在我的 addLocation 操作 (buttonPressed) 中使用它。我正在尝试使用压力触摸在 map 上添加图钉,在两
我设置了一个详细 View ,我是否有几个 Nib 文件根据在 Root View Controller 的表中选择的项目来加载。 我发现,对于 Nibs 的类,永远不会调用 viewDidUnloa
我需要动态访问 json 文件并使用以下代码。在本例中,“bpicsel”和“temp”是变量。最终结果类似于“data[0].extit1” var title="data["+bpicsel+"]
我需要使用第三方 WCF 服务。我已经在我的证书存储中配置了所需的证书,但是在调用 WCF 服务时出现以下异常。 向 https://XXXX.com/AHSharedServices/Custome
在几个 SO 答案(1、2)中,建议如果存在冲突则不应触发 INSERT 触发器,ON CONFLICT DO NOTHING 在触发语句中。也许我理解错了,但在我的实验中似乎并非如此。 这是我的 S
如果进行修改,则会给出org.hibernate.NonUniqueObjectException。在我的 BidderBO 类(class)中 @Override @Transactional(pr
我使用 indexOf() 方法来精细地查找数组中的对象。 直到此刻我查了一些资料,发现代码应该无法正常工作。 我在reducer中尝试了上面的代码,它成功了 let tmp = state.find
假设我有以下表格: CREATE TABLE Game ( GameID INT UNSIGNED NOT NULL, GameType TINYINT UNSIGNED NOT NU
代码: Alamofire.request(URL(string: imageUrl)!).downloadProgress(closure: { (progress) in
我是一名优秀的程序员,十分优秀!