将整数类型缩放为不同的位大小

  • 本文关键字:整数 类型 缩放 c++ c++17
  • 更新时间 :
  • 英文 :


如何将积分类型放大/缩小到其他大小,例如从8位到16位应该是:

  • 0x00->0x0000
  • 0xFF->0xFFFF

这是我目前提出的通用解决方案。按比例缩小可以正常工作,但只有当返回类型的大小是输入类型的两倍时,按比例放大才有效,即8到16,或16到32,但不是8到32。

我不知道这样一个函数的其他用途是什么,除了8位到16位,反之亦然。我只是在修修补补。

#include <iostream>
#include <limits>
#include <type_traits>
#include <math.h>

template<class PxOut, class PxIn,
typename = typename std::enable_if<std::is_integral<PxOut>::value, PxOut>::type , /// Must be integral
typename = typename std::enable_if<std::is_integral<PxIn>::value, PxIn>::type,
typename = typename std::enable_if<!std::is_signed<PxOut>::value, PxOut>::type , /// Must be positive
typename = typename std::enable_if<!std::is_signed<PxIn>::value, PxOut>::type
>
static inline constexpr PxOut binScale(PxIn x)
{
if constexpr ( std::is_same<PxOut,PxIn>::value ){
return x;
}
else if constexpr ( (/*2**/sizeof( PxIn ) ) < sizeof( PxOut ) ){
/// ## Scale Up
/// ## https://stackoverflow.com/a/6436100/12209845
///
PxOut xx = PxIn( x );
const uint8_t sft = sizeof( PxIn ) * 8;
return ( ( xx + ( xx << sft ) ) << sft ) + std::numeric_limits<PxIn>::max();
}
else if constexpr ( sizeof( PxIn ) > sizeof( PxOut ) ){
/// ## Scale Down
/// ## https://code.woboq.org/qt5/qtbase/src/gui/painting/qrgba64.h.html#_ZN7QRgba647div_257Et
///
const uint8_t sft = ( sizeof( PxIn ) * 8 ) - sizeof(PxOut) * 8;
return ( x - ( x >> sft ) ) >> sft;
}
}
/// Change the types and the test value in the next lines
///
using PxIn = uint8_t;
using PxOut =  uint16_t;
const PxIn dv = 2;

int main(int , char *[])
{
std::cout << (uint64_t)binScale<PxOut,PxIn>(
(std::numeric_limits<PxIn>::max() ) /
dv );
std::cout << std::endl;
std::cout << (uint64_t)std::numeric_limits<PxOut>::max() / dv ;
std::cout << std::endl;
return 0;
}

编辑

应根据返回类型的最大值的百分比进行缩放。8位到16位,看起来像:

/*
8 bits       :  16 bits
255 -> 65535 :  Scale to 1/1 of 65535 or (0xFFFF)
127 -> 32767 :  Scale to 1/2 of 65535 or (0xFFFF)
85  -> 22015 :  Scale to 1/3 of 65535 or (0xFFFF)
63  -> 16383 :  Scale to 1/4 of 65535 or (0xFFFF)
0   -> 0     :  Scale to 0% of 65535 or (0xFFFF)
*/

假设你有一个8位的图像,你想将每个像素缩放到16位,这样它就会从0xFF->0xFFFF,这是我能想到的一个特殊用途。

OP尝试执行的一个可能的应用是将颜色从较低的像素深度(例如每像素8位(缩放到较高的像素深度,例如每像素16位(。

乍一看,你可能认为因子必须是216/28=258=256,但这是错误的。

它不会将0xff(带8位的白色(映射到0xff00,其中16位中的白色将是0xffff。

相反,系数必须为0xffff/0xff=0x101=257。

这就是我的示例实现的基础:

using uintN = unsigned long long;
constexpr uintN getMax(uintN nBits)
{
assert(nBits <= sizeof (uintN) * 8);
return (uintN)-1 >> (sizeof (uintN) * 8 - nBits);
}

然后放大因子变为:

uintN f = getMax(to) / getMax(from);

其中CCD_ 1和CCD_。

令人惊讶的是,对于8、16、32、64的fromto的所有可能对(其中fromto(,因子总是一个整数值。

#include <cassert>
#include <iomanip>
#include <iostream>
using uintN = unsigned long long;
constexpr uintN getMax(uintN nBits)
{
assert(nBits <= sizeof (uintN) * 8);
return (uintN)-1 >> (sizeof (uintN) * 8 - nBits);
}
int main()
{
uintN nBits[] = { 8, 16, 32, 64 };
uintN tests[][3] = {
{ 0x7f, 0x7f7f, 0x7f7f7f7f },
{ 0xab, 0xabab, 0xabababab },
{ 0xff, 0xffff, 0xffffffff }
};
for (int i = 0; i < 4; ++i) {
uintN from = nBits[i];
for (int j = i + 1; j < 4; ++j) {
uintN to = nBits[j];
uintN f = getMax(to) / getMax(from);
std::cout << std::dec << "from " << from << " bits to " << to << " bits: factor: " << std::hex << f << "n";
std::cout << "Samples:n";
for (auto test : tests) {
std::cout << test[i] << " -> " << f * test[i] << 'n';
}
}
}
}

输出:

from 8 bits to 16 bits: factor: 101
Samples:
7f -> 7f7f
ab -> abab
ff -> ffff
from 8 bits to 32 bits: factor: 1010101
Samples:
7f -> 7f7f7f7f
ab -> abababab
ff -> ffffffff
from 8 bits to 64 bits: factor: 101010101010101
Samples:
7f -> 7f7f7f7f7f7f7f7f
ab -> abababababababab
ff -> ffffffffffffffff
from 16 bits to 32 bits: factor: 10001
Samples:
7f7f -> 7f7f7f7f
abab -> abababab
ffff -> ffffffff
from 16 bits to 64 bits: factor: 1000100010001
Samples:
7f7f -> 7f7f7f7f7f7f7f7f
abab -> abababababababab
ffff -> ffffffffffffffff
from 32 bits to 64 bits: factor: 100000001
Samples:
7f7f7f7f -> 7f7f7f7f7f7f7f7f
abababab -> abababababababab
ffffffff -> ffffffffffffffff

coliru上的实时演示

当将m位int放大为n位数int(仅限无符号(时,您必须将其乘以(2^n-1(/(2^m-1(因子,因为我们希望每个数字之间的间距相等。当n=2m时,您使用的表达式是上述表达式的简化,假设2^m是a,则(2^n-1(/(2^m-1(变为(a^2-1(/a-1,即+1。在您的情况下,乘以a+1,即2^8+1是通过先移8,然后加上原始值来实现的(来自您发布的堆栈溢出链接(。因此当从8缩放到16或从16缩放到32时,它起作用。对于8到32,表达式将变为^3+a^2+a+1。但我建议列出一个可以进行一次缩放的所有可能组合的列表(8->16,16->32,32->64,8->32-16>64,8->,并将缩放因子存储在阵列中,而不是重新计算每个像素的因子,并根据输入和输出大小与缩放因子相乘。

最新更新