C++ 带浮点数的2D点Morton索引
我有一个2D点,看起来像这样:C++ 带浮点数的2D点Morton索引,c++,algorithm,z-order-curve,C++,Algorithm,Z Order Curve,我有一个2D点,看起来像这样: class Point { float m_x, m_y; public: int mortonIndex() { // what would go here? } }; 我知道如何处理整数,但我需要使用浮点数。我还希望避免任何特定网格大小的缩放 维基百科的相关页面: 有两种方法来看待这一点: “float”是256位的数字,(或者类似地,“double”是2100位的数字,在这里实现[1])。 “fl
class Point
{
float m_x, m_y;
public:
int mortonIndex()
{
// what would go here?
}
};
我知道如何处理整数,但我需要使用浮点数。我还希望避免任何特定网格大小的缩放
维基百科的相关页面:
有两种方法来看待这一点:
- “float”是256位的数字,(或者类似地,“double”是2100位的数字,在这里实现[1])。
- “float”是一个奇怪的32位整数。
float
s最初设计为与旧的纯整数数据库引擎兼容的事实,允许它们将浮点数视为1s补码整数
更准确地说,在1s补码意义上,浮点值的顺序尊重相同宽度的整数的顺序(事实上,直接向双关浮点加1将得到具有更大绝对大小的相邻值**)
类点
{
浮动m_x,m_y;
//当使用浮点模型时,此断言不正确
//不符合IEEE标准,浮点不是32位,或两者兼而有之。
//
//这样的东西很难找到,所以我们只能假设
//大部分是健全的硬件。
//
静态断言(
(sizeof(int)=sizeof(float))&&
(sizeof(int)*字符位==32)&&
(sizeof(long-long)*字符位==64),
“我们需要32位整数和浮点数,以及64位长的长整数!”
);
公众:
//所以我们不会丢失任何信息,我们需要2倍的宽度。
//毕竟,我们把两个32位数字塞进了一个值中。
//这里的丢失意味着需要实现一个容器
//装箱策略。
//
//显然,更高的维度需要一个数组。
//
//另外,我们不打算修改这个点,所以让它成为一个常量例程。
//
长mortonIndex()常数
{
//将x和y坐标双关为整数:只需重新解释位。
//
auto ix=重新解释铸型(此->m_x);
auto iy=重新解释投射(此->m_y);
//由于我们假设2s补码算法(目前99.99%的硬件),
//我们需要将这些原始整数双关浮点转换为
//它们对应的整数“指数”。
//把他们的符号涂在上面,让他们在下面玩。
//
const auto ixs=静态_cast(ix)>>31;
const auto iys=静态施法(iy)>>31;
//这是快速绝对值和偏差的组合。
//
//我们需要调整这些值,使-FLT_MAX接近于0。
//
ix=((ix&0x7FFFFFL)^ixs)-ixs)+0x7FFFFFL;
iy=((iy&0x7FFFFFL)^iys)-iys)+0x7FFFFFL;
//现在我们有-FLT_MAX接近0,FLT_MAX接近UINT_MAX,
//其他一切都在中间。
//
//为了简化这个过程,我们将x和y作为64位整数。
//
长xx=ix;
long-long yy=iy;
//像往常一样扩张和合并。。。
xx=(xx |)(xx什么是莫顿指数?是的,我可以用谷歌搜索它,但也许你可以把它放在你的问题中,这样我就不用问了。它们也被称为“Z指数”或“勒贝格曲线上的点”.对我来说似乎是个直截了当的问题。如果你不知道莫顿指数是什么,你不必为它烦恼。这些神奇的数字是什么?我是说“0x0000FFFF0000FFFFL”以此类推……再加上同一语句中的移位,它们掩盖了我们不想在那一步放大的值的一半。如果你用手做这个,比如说所有的1,你会注意到这会“分散”位。上的例子有助于理解神奇的数字。
class Point
{
float m_x, m_y;
// This assert is not correct when the floating point model
// is not IEEE-compliant, float is not 32-bit, or both.
//
// Such things are hard to find, so we'll just assume
// mostly-sane hardware.
//
static_assert(
(sizeof(int) == sizeof(float)) &&
(sizeof(int)*CHAR_BIT == 32) &&
(sizeof(long long)*CHAR_BIT == 64),
"We need 32-bit ints and floats, and 64-bit long longs!"
);
public:
// So we don't lose any information, we need 2x the width.
// After all, we're cramming two 32-bit numbers into a single value.
// Lossiness here would mean a container would need to implement
// a binning strategy.
//
// Higher dimensions would require an array, obviously.
//
// Also, we're not going to modify the point, so make this a const routine.
//
long long mortonIndex() const
{
// Pun the x and y coordinates as integers: Just re-interpret the bits.
//
auto ix = reinterpret_cast<const unsigned &>(this->m_x);
auto iy = reinterpret_cast<const unsigned &>(this->m_y);
// Since we're assuming 2s complement arithmetic (99.99% of hardware today),
// we'll need to convert these raw integer-punned floats into
// their corresponding integer "indices".
// Smear their sign bits into these for twiddling below.
//
const auto ixs = static_cast<int>(ix) >> 31;
const auto iys = static_cast<int>(iy) >> 31;
// This is a combination of a fast absolute value and a bias.
//
// We need to adjust the values so -FLT_MAX is close to 0.
//
ix = (((ix & 0x7FFFFFFFL) ^ ixs) - ixs) + 0x7FFFFFFFL;
iy = (((iy & 0x7FFFFFFFL) ^ iys) - iys) + 0x7FFFFFFFL;
// Now we have -FLT_MAX close to 0, and FLT_MAX close to UINT_MAX,
// with everything else in-between.
//
// To make this easy, we'll work with x and y as 64-bit integers.
//
long long xx = ix;
long long yy = iy;
// Dilate and combine as usual...
xx = (xx | (xx << 16)) & 0x0000ffff0000ffffLL;
yy = (yy | (yy << 16)) & 0x0000ffff0000ffffLL;
xx = (xx | (xx << 8)) & 0x00ff00ff00ff00ffLL;
yy = (yy | (yy << 8)) & 0x00ff00ff00ff00ffLL;
xx = (xx | (xx << 4)) & 0x0f0f0f0f0f0f0f0fLL;
yy = (yy | (yy << 4)) & 0x0f0f0f0f0f0f0f0fLL;
xx = (xx | (xx << 2)) & 0x3333333333333333LL;
yy = (yy | (yy << 2)) & 0x3333333333333333LL;
xx = (xx | (xx << 1)) & 0x5555555555555555LL;
yy = (yy | (yy << 1)) & 0x5555555555555555LL;
return xx | (yy << 1);
}
};