Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/64.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 钳位短到无符号字符_C - Fatal编程技术网

C 钳位短到无符号字符

C 钳位短到无符号字符,c,C,我有一个简单的C函数,如下所示: unsigned char clamp(short value){ if (value < 0) return 0; if (value > 0xff) return 0xff; return value; } 编辑3: 刚刚在FFmpeg代码中看到了这一点的一个非常好的实现: /** * Clip a signed integer value into the 0-255 range. * @param a value

我有一个简单的C函数,如下所示:

unsigned char clamp(short value){
    if (value < 0) return 0;
    if (value > 0xff) return 0xff;
    return value;
}
编辑3:

刚刚在FFmpeg代码中看到了这一点的一个非常好的实现:

/**
 * Clip a signed integer value into the 0-255 range.
 * @param a value to clip
 * @return clipped value
 */
static av_always_inline av_const uint8_t av_clip_uint8_c(int a)
{
    if (a&(~0xFF)) return (-a)>>31;
    else           return a;
}

这当然有效,如果很好的话,它会减少到1

如果您只是想避免实际的If/else,请使用
?:操作员:

return value < 0 ? 0 : (value > 0xff ? 0xff : value);
返回值<0?0:(值>0xff?0xff:值);
然而,就效率而言,这应该没有什么不同


在实践中,你不应该为这样琐碎的事情担心效率。让编译器来进行优化。

使其高效的一种方法是将此函数声明为内联函数,以避免函数调用开销。您也可以使用第三级运算符将其转换为宏,但这将删除编译器的返回类型检查。

您可以使用另一张海报所示的
?:
或使用
abs()
的有趣属性来执行此操作,该属性允许您计算两个值的最大值或最小值

例如,表达式
(a+abs(a))/2
对于正数返回
a
,否则返回
0
(最大值为
a
0

这给

unsigned char clip(short value)
{
  short a = (value + abs(value)) / 2;
  return (a + 255 - abs(a - 255)) / 2;
}
为了让自己相信这是可行的,这里有一个测试程序:

#include <stdio.h>

unsigned char clip(short value)
{
  short a = (value + abs(value)) / 2;
  return (a + 255 - abs(a - 255)) / 2;
}

void test(short value)
{
  printf("clip(%d) = %d\n", value, clip(value));
}

int main()
{
  test(0);
  test(10);
  test(-10);
  test(255);
  test(265);
  return 0;
}
当然,有人可能会说,在
abs()
中可能有一个测试,但是
gcc-O3
可以线性编译它:

clip:
    movswl  %di, %edi
    movl    %edi, %edx
    sarl    $31, %edx
    movl    %edx, %eax
    xorl    %edi, %eax
    subl    %edx, %eax
    addl    %edi, %eax
    movl    %eax, %edx
    shrl    $31, %edx
    addl    %eax, %edx
    sarl    %edx
    movswl  %dx, %edx
    leal    255(%rdx), %eax
    subl    $255, %edx
    movl    %edx, %ecx
    sarl    $31, %ecx
    xorl    %ecx, %edx
    subl    %ecx, %edx
    subl    %edx, %eax
    movl    %eax, %edx
    shrl    $31, %edx
    addl    %edx, %eax
    sarl    %eax
    ret
但请注意,这将比原始函数效率低得多,原始函数编译为:

clip:
    xorl    %eax, %eax
    testw   %di, %di
    js      .L1
    movl    $-1, %eax
    cmpw    $255, %di
    cmovle  %edi, %eax
.L1:
    rep
    ret

但至少它回答了你的问题:)

你可以做一个2D查找表:

unsigned char clamp(short value)
{
  static const unsigned char table[256][256] = { ... }

  const unsigned char x = value & 0xff;
  const unsigned char y = (value >> 8) & 0xff;
  return table[y][x];
}
当然,这看起来很奇怪(这是一个64 KB的表,用于这个琐碎的计算)。然而,考虑到您提到要在GPU上执行此操作,我认为上面的内容可能是纹理查找,我相信在GPU上非常快速

此外,如果您的GPU使用OpenGL,您当然可以直接使用内置的

clamp(value, 0, 255);

这不会进行类型转换(GLSL中似乎没有8位整数类型),但仍然如此。

假设短两个字节,并以代码可读性为代价:

clipped_x =  (x & 0x8000) ? 0 : ((x >> 8) ? 0xFF : x);

你写你想要避免在GPU上分支。的确,在并行环境中,分支可能非常昂贵,因为要么必须对两个分支进行评估,要么必须应用同步。但是,如果分支足够小,代码将比大多数算法更快。报告描述了原因:

有时,编译器可能[…] 优化out if或switch语句 而是使用分支谓词。 在这些情况下,任何扭曲都无法实现 偏离[……]

当使用分支谓词时,没有 其执行的指令 取决于控制条件 被跳过。相反,它们都是 与每线程条件关联 设置为true的代码或谓词 或基于控制的错误 条件和条件 指令被安排在 执行时,只有带有 真谓词实际上是 执行。用假密码的指示 谓词不写入结果,并且 也不要计算地址或读取 操作数

分支预测很快。太快了!如果您查看由优化编译器生成的中间PTX代码,您会发现它甚至优于普通的算法。因此,davmac答案中的代码可能是最快的

我知道您没有特别询问CUDA,但大多数最佳实践指南也适用于OpenCL,可能还有大部分AMDs GPU编程


顺便说一句:在我见过的几乎所有GPU代码中,大部分时间都花在内存访问上,而不是算术上。确保你的个人资料

你应该给这个难看但仅限于算术的版本计时

unsigned char clamp(short value){
  short pmask = ((value & 0x4000) >> 7) | ((value & 0x2000) >> 6) |
    ((value & 0x1000) >> 5) | ((value & 0x0800) >> 4) |
    ((value & 0x0400) >> 3) | ((value & 0x0200) >> 2) |
    ((value & 0x0100) >> 1);
  pmask |= (pmask >> 1) | (pmask >> 2) | (pmask >> 3) | (pmask >> 4) |
    (pmask >> 5) | (pmask >> 6) | (pmask >> 7);
  value |= pmask;
  short nmask = (value & 0x8000) >> 8;
  nmask |= (nmask >> 1) | (nmask >> 2) | (nmask >> 3) | (nmask >> 4) |
    (nmask >> 5) | (nmask >> 6) | (nmask >> 7);
  value &= ~nmask;
  return value;
}
那么:

unsigned char clamp (short value) {
    unsigned char r = (value >> 15);          /* uses arithmetic right-shift */
    unsigned char s = !!(value & 0x7f00) * 0xff;
    unsigned char v = (value & 0xff);
    return (v | s) & ~r;
}

但我严重怀疑它的执行速度是否比涉及分支的原始版本快。

请简要说明一下术语,我相信这通常被称为钳位,而不是钳位,如果与计算相关,则通常被称为“饱和算术”,如果你在谷歌搜索,可能会有帮助。你为什么要这么做?这看起来已经很有效了。GPU上有
abs()
吗?@roe,我已经更新了术语。Thanx。不确定这是否是您的意图,但FFmpeg的最后一个示例看起来像是进行了换行。所以正如我提到的,我希望去掉分支。因此,这与if/else相同。如果在目标体系结构上可能的话,编译器可能会优化掉分支。编译器优化不会更容易或更难?:但是。它只是一个if-else,语法混乱。最有可能发明?:操作符是为了节省存储源代码文件的硬盘空间。。。所以除非你的硬盘不能处理更多的字节,否则就没有理由使用?:abs还会增加额外的函数调用开销。我认为abs有一个小于零的检查值。@Shailesh Kumar:事实上,GCC在这种情况下不会为
abs()
生成函数调用或测试,如编辑后的版本所示。有趣的是,Nvidia的CUTIL头使用常规的max/min函数来实现这一点!表查找当然是一种可能的解决方案。我得检查一下它的性能是否比if/else好。?和:本质上是分支。thanx。我应该花时间阅读CUDA C最佳实践指南!
unsigned char clamp(short value){
  short pmask = ((value & 0x4000) >> 7) | ((value & 0x2000) >> 6) |
    ((value & 0x1000) >> 5) | ((value & 0x0800) >> 4) |
    ((value & 0x0400) >> 3) | ((value & 0x0200) >> 2) |
    ((value & 0x0100) >> 1);
  pmask |= (pmask >> 1) | (pmask >> 2) | (pmask >> 3) | (pmask >> 4) |
    (pmask >> 5) | (pmask >> 6) | (pmask >> 7);
  value |= pmask;
  short nmask = (value & 0x8000) >> 8;
  nmask |= (nmask >> 1) | (nmask >> 2) | (nmask >> 3) | (nmask >> 4) |
    (nmask >> 5) | (nmask >> 6) | (nmask >> 7);
  value &= ~nmask;
  return value;
}
unsigned char clamp (short value) {
    unsigned char r = (value >> 15);          /* uses arithmetic right-shift */
    unsigned char s = !!(value & 0x7f00) * 0xff;
    unsigned char v = (value & 0xff);
    return (v | s) & ~r;
}