在以下代码中:
#include <iostream>
...
uint64_t t1 = 1510763846;
uint64_t t2 = 1510763847;
double d1 = (double)t1;
double d2 = (double)t2;
// d1 == t2 => evaluates to true somehow?
// t1 == d2 => evaluates to true somehow?
// d1 == d2 => evaluates to true somehow?
// t1 == t2 => evaluates to false, of course.
std::cout << std::fixed <<
"uint64_t: " << t1 << ", " << t2 << ", " <<
"double: " << d1 << ", " << d2 << ", " << (d2+1) << std::endl;
我得到这个输出:
uint64_t: 1510763846, 1510763847, double: 1510763904.000000, 1510763904.000000, 1510763905.000000
我不明白为什么。答案是:可以存储在double中的最大整数表示,一个高达2^53(9007199254740992)的整数可以存储在double
中,而不会丢失精度。
实际上,当我开始用替身进行计算时,我会出现错误,所以这不仅仅是打印问题。(例如1510763846和1510763847都给出1510763904)
同样奇怪的是,双精度可以被添加到中,然后得到正确的结果(d2+1==1510763905.000000)
理由:我将这些数字转换为双精度,因为我需要在Lua中使用它们,它只支持浮点数字。我确信我正在用double
作为lua_Number
类型而不是float
编译Lua-lib。
std::cout << sizeof(t1) << ", " << sizeof(d2) << std::endl;
输出
8, 8
我将VS 2012与目标MachineX86工具包v110_xp一起使用。浮点模型"Precise(/fp:Precise)">
附录
在回复者和本文的帮助下,为什么在特定的Visual Studio 2008项目中错误地添加了替身?,我已经能够找出问题所在。库使用_set_controlfp、_control87、_controlfp或__control87_2等函数将可执行文件的精度更改为"single"。这就是为什么uint64_t转换为double的行为就像它是一个float。
在对上述函数名和用于Precision Control的"MCW_PC"进行文件搜索时,我发现了以下可能设置了它的库:
- 安卓NDK
- 提升::数学
- 提升::数字
- DirectX(我们将于2010年6月使用)
- FMod(非EX)
- Pyro粒子发动机
现在我想重新表述我的问题:
如何确保每次从uint64_t
转换为double
都能正确进行,而不需要:
- 每次可能的转换发生时都必须调用_fpreset()(想想函数参数)
- 必须担心库的线程在我的_fpreset()和转换之间更改浮点精度
天真的代码应该是这样的:
double toDouble(uint64_t i)
{
double d;
do {
_fpreset();
d = i;
_fpreset();
} while (d != i);
return d;
}
double toDouble(int64_t i)
{
double d;
do {
_fpreset();
d = i;
_fpreset();
} while (d != i);
return d;
}
该解决方案假设线程两次干扰浮点精度的几率非常小。问题是,我使用的值是代表现实世界价值的计时器。所以我不应该冒险。有解决这个问题的灵丹妙药吗?
从ieee754浮点转换来看,double的实现实际上是浮点的,这当然是标准允许的,该标准要求sizeof double >= sizeof float
。
1510763846的最准确表示是1.510763904E9。