提问人:Luc Bloom 提问时间:11/16/2017 最后编辑:Luc Bloom 更新时间:11/16/2017 访问量:3022
将 uint64_t 转换为双倍后出现意外结果
Unexpected result after converting uint64_t to double
问:
在以下代码中:
#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
我不明白为什么。这个答案:可以存储在双精度中的最大整数表示可以在不损失精度的情况下存储在 a 中最多 2^53 (9007199254740992) 的整数。double
实际上,当我开始使用双精度进行计算时,我会遇到错误,所以这不仅仅是打印问题。(例如,1510763846 和 1510763847 都给出1510763904)
同样很奇怪的是,双精度可以加到然后正确 (d2+1 == 1510763905.000000)
基本原理:我将这些数字转换为双精度,因为我需要在 Lua 中使用它们,它只支持浮点数。我确定我正在编译 Lua 库作为类型,而不是 .double
lua_Number
float
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转换为双精度时的行为就像浮点数一样。
在对上述函数名称和用于精确控制的“MCW_PC”进行文件搜索时,我发现以下库可能已经设置了它:
- Android NDK
- boost::数学
- boost::数值
- DirectX(我们使用的是 2010 年 6 月)
- FMod(非 EX)
- Pyro 粒子引擎
现在我想重新表述我的问题:
如何确保每次都能正确地从 a 转换为 a,而无需: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。
评论
float
double
float
double
评论
#include
<iostream>
<time.h>
#include
<cassert>
<cstdint>