在C++中使用一个而不是另一个有什么优缺点?
给定一个double,我想将它舍入到小数点后的给定数量的精度点,类似于PHP的round()函数.
我在Dart文档中找到的最接近的东西是double.toStringAsPrecision(),但这不是我需要的,因为它包括精度总点数小数点之前的数字.
例如,使用toStringAsPrecision(3):
0.123456789 rounds to 0.123
9.123456789 rounds to 9.12
98.123456789 rounds to 98.1
987.123456789 rounds to 987
9876.123456789 rounds to 9.88e+3
Run Code Online (Sandbox Code Playgroud)
随着数量的增加,我在小数位后相应地失去了精度.
float f = 0.7;
if( f == 0.7 )
printf("equal");
else
printf("not equal");
Run Code Online (Sandbox Code Playgroud)
为什么输出not equal?
为什么会这样?
我知道这是一个奇怪的问题,但JavaScript是否有能力使用double而不是单个浮点数?(64位浮点数与32位.)
每次我开始一个新项目,当我需要比较一些浮点数或双变量时,我会像这样编写代码:
if (fabs(prev.min[i] - cur->min[i]) < 0.000001 &&
fabs(prev.max[i] - cur->max[i]) < 0.000001) {
continue;
}
Run Code Online (Sandbox Code Playgroud)
然后我想摆脱这些神奇的变量0.000001(和双倍的0.00000000001)和fabs,所以我写了一个内联函数,有些定义:
#define FLOAT_TOL 0.000001
Run Code Online (Sandbox Code Playgroud)
所以我想知道是否有任何标准方法可以做到这一点?可能是一些标准的头文件?浮点数和双限制(最小值和最大值)也很好
这部分是学术性的,就我的目的而言,我只需将它四舍五入到小数点后两位; 但是我很想知道结果会产生两个稍微不同的结果.
这是我写的测试,以缩小到最简单的实现:
@Test
public void shouldEqual() {
double expected = 450.00d / (7d * 60); // 1.0714285714285714
double actual = 450.00d / 7d / 60; // 1.0714285714285716
assertThat(actual).isEqualTo(expected);
}
Run Code Online (Sandbox Code Playgroud)
但它失败了这个输出:
org.junit.ComparisonFailure:
Expected :1.0714285714285714
Actual :1.0714285714285716
Run Code Online (Sandbox Code Playgroud)
任何人都可以详细解释引擎盖下发生的事情导致1.000000000000000的价值X不同吗?
我在答案中寻找的一些要点是:精度丢失在哪里?首选哪种方法,为什么?哪个是正确的?(在纯数学中,两者都不对.也许两者都错了?)这些算术运算有更好的解决方案或方法吗?
我有以下代码:
#include <cstdio>
int main()
{
if ((1.0 + 0.1) != (1.0 + 0.1))
printf("not equal\n");
else
printf("equal\n");
return 0;
}
Run Code Online (Sandbox Code Playgroud)
当使用gcc(4.4,4.5和4.6)使用O3编译并本机运行(ubuntu 10.10)时,它会打印预期的"相等"结果.
但是,当如上所述编译并在虚拟机(ubuntu 10.10,virtualbox image)上运行时,它输出"不相等"的情况下相同的代码 - 这是O3和O2标志设置但不是O1及以下时的情况.当使用clang(O3和O2)编译并在虚拟机上运行时,我得到了正确的结果.
我理解1.1无法使用double正确表示,我读过"每个计算机科学家应该知道浮点算术的内容"所以请不要指向我那里,这似乎是GCC做的某种优化不知何故似乎在虚拟机中不起作用.
有任何想法吗?
注意:C++标准说在这种情况下类型提升是依赖于实现的,可能是GCC使用更精确的内部表示,当应用不等式测试时,它是否成立 - 由于额外的精度?
UPDATE1:以上修改上面的代码,现在得到了正确的结果.在某些时候,无论出于何种原因,GCC都会关闭浮点控制字.
#include <cstdio>
void set_dpfpu() { unsigned int mode = 0x27F; asm ("fldcw %0" : : "m" (*&mode));
int main()
{
set_dpfpu();
if ((1.0 + 0.1) != (1.0 + 0.1))
printf("not equal\n");
else
printf("equal\n");
return 0;
}
Run Code Online (Sandbox Code Playgroud)
UPDATE2:对于那些询问代码的const表达性质的人,我已经改变了如下,并且在使用GCC编译时仍然失败. - 但我认为优化器也可能将以下内容转换为const表达式.
#include <cstdio>
void …Run Code Online (Sandbox Code Playgroud) c++ compiler-construction optimization double-precision vm-implementation
我有一个单元测试,测试边界:
[TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void CreateExtent_InvalidTop_ShouldThrowArgumentOutOfRangeException()
{
var invalidTop = 90.0 + Double.Epsilon;
new Extent(invalidTop, 0.0, 0.0, 0.0);
}
public static readonly double MAX_LAT = 90.0;
public Extent(double top, double right, double bottom, double left)
{
if (top > GeoConstants.MAX_LAT)
throw new ArgumentOutOfRangeException("top"); // not hit
}
Run Code Online (Sandbox Code Playgroud)
我以为我只是通过添加最小可能的正双倍来向尖端90.0倾斜,但现在异常没有被抛出,任何想法为什么?
在调试时,我看到top为90,当它应该是90.00000000 ....
编辑:
我应该考虑更难,90+Double.Epsilon将失去其解决方案.似乎最好的方法是做一些转移.
解:
[TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void CreateExtent_InvalidTop_ShouldThrowArgumentOutOfRangeException()
{
var invalidTop = Utility.IncrementTiny(90); // 90.000000000000014
// var sameAsEpsilon = Utility.IncrementTiny(0);
new Extent(invalidTop, 0, 0, 0);
}
/// <summary> …Run Code Online (Sandbox Code Playgroud) 我正在使用32位计算机运行,并且我能够使用以下快速命中的代码片段来确认长值可以撕裂.
static void TestTearingLong()
{
System.Threading.Thread A = new System.Threading.Thread(ThreadA);
A.Start();
System.Threading.Thread B = new System.Threading.Thread(ThreadB);
B.Start();
}
static ulong s_x;
static void ThreadA()
{
int i = 0;
while (true)
{
s_x = (i & 1) == 0 ? 0x0L : 0xaaaabbbbccccddddL;
i++;
}
}
static void ThreadB()
{
while (true)
{
ulong x = s_x;
Debug.Assert(x == 0x0L || x == 0xaaaabbbbccccddddL);
}
}
Run Code Online (Sandbox Code Playgroud)
但是当我尝试与双打类似的东西时,我无法得到任何撕裂.有谁知道为什么?据我从规范中可以看出,只有浮点数的赋值才是原子的.分配给双人应该有撕裂的风险.
static double s_x;
static void TestTearingDouble()
{
System.Threading.Thread A = new System.Threading.Thread(ThreadA);
A.Start(); …Run Code Online (Sandbox Code Playgroud) 我知道以下行为是一个老问题,但我仍然不明白.
System.out.println(0.1 + 0.1 + 0.1);
Run Code Online (Sandbox Code Playgroud)
或者即使我使用 BigDecimal
System.out.println(new BigDecimal(0.1).doubleValue()
+ new BigDecimal(0.1).doubleValue()
+ new BigDecimal(0.1).doubleValue());
Run Code Online (Sandbox Code Playgroud)
为什么这个结果是:0.30000000000000004而不是:0.3?
我怎么解决这个问题?
double-precision ×10
c++ ×4
c# ×2
double ×2
java ×2
.net ×1
atomicity ×1
bigdecimal ×1
c ×1
dart ×1
epsilon ×1
javascript ×1
numerical ×1
optimization ×1
precision ×1
rounding ×1
types ×1
unit-testing ×1