【问题标题】:Cast double to int64_t and back without info lost将 double 转换为 int64_t 并返回而不会丢失信息
【发布时间】:2017-03-18 18:20:25
【问题描述】:

可能有人问过这个问题,但我搜索并找不到答案。

我正在实现一个玩具虚拟机,其中 OpCode 采用以下形式:

std::tuple<int8_t, int64_t, int64_t> // instruction op1, op2

我正在尝试将一个 double 打包到一个操作数中,并在处理它时再次读回它。这不可靠。

double d = ...
auto a = static_cast<int64_t>(d);
auto b = static_cast<double>(a)
// sometimes, b != d

有没有办法将双精度的位表示打包到一个 int64_t 中,然后读取该位模式得到与以前完全相同的双精度?

【问题讨论】:

标签: c++ c++17


【解决方案1】:

static_cast 执行值转换 - 小数部分总是丢失。 memcpy 是您所追求的。

double d = ... 
int64_t a;
memcpy(&a, &d, sizeof(a));
double d2;
memcpy(&d2, &a, sizeof(d2));

不过,我可能会改为将操作数设为 uniondoubleint64_t(可能还有其他对您的 VM 感兴趣的类型)。

【讨论】:

  • 从联合体的任何成员中读取是未定义的行为,除了最后一个写入的成员。
  • @RichardCritten 我认为建议是,如果打算使用double 参数,那么它将被写入和读取为double(使用元组的int8_t 成员用于区别对待)
  • 正如MM所说。
【解决方案2】:

使其工作的一种方法是将内存块重新解释为 int64_t/double,即进行指针转换:

double d = ...
auto *a = (int64_t*)&d;
auto *d2 = (double*)a;
auto b = *d2;
assert(d == b);

请注意,我们在这里都假设doubleint64_t 的大小相同(64 位)。我现在不记得它是否是标准的一部分。

【讨论】:

  • 这是未定义的行为。 std::memcpy 是唯一定义的方式。
  • @RichardCritten 为什么是 UB?我的意思是只要尺寸是正确的,那么它有什么未定义的?
  • @freakish 因为标准是这样说的,您需要检查编译器是否支持扩展。 “...从最近未编写的联合成员中读取是未定义的行为。许多编译器作为非标准语言扩展实现了读取联合中非活动成员的能力。” en.cppreference.com/w/cpp/language/union
  • @RichardCritten 你为什么要谈论工会?这与具有相同大小的指针转换有什么关系?
  • @freakish 这很好地定义为转换为另一种指针类型并返回。什么是实现定义是当您尝试读取不同指针类型的值时。看到这个:stackoverflow.com/a/7832859/5405086。这不是 UB!
猜你喜欢
  • 2010-10-29
  • 1970-01-01
  • 1970-01-01
  • 2016-06-01
  • 1970-01-01
  • 2020-03-29
  • 2022-01-22
  • 1970-01-01
  • 2013-11-14
相关资源
最近更新 更多