【问题标题】:Whats wrong with my division?我的部门怎么了?
【发布时间】:2013-06-07 01:04:11
【问题描述】:

我有一个介于 -32768 和 32767 之间的 16 位样本。 为了节省空间,我想将其转换为 8 位样本,因此我将样本除以 256,然后加上 128。

-32768 / 256 = -128 + 128 = 0
32767 / 256 = 127.99 + 128 = 255.99

现在,0 将完全适合一个字节,但 255.99 必须向下舍入为 255,导致精度下降,因为在转换回来时,我将得到 32512 而不是 32767。

如何在不丢失原始最小值/最大值的情况下做到这一点?我知道我犯了一个非常明显的思想错误,但我不知道错误在哪里。

是的,当然,我完全知道除法会丢失精度,并且无法从 8 位样本中推断出原始值,但我只是想知道为什么我没有得到原始最大值。

【问题讨论】:

  • 您没有回答自己的问题吗?当您需要 0..256 时,您必须向下舍入以适应 0..255 范围。如果您可以进行浮点运算,请除以 257,以便 0..255 捕获范围。
  • 按定义下采样会降低精度。想象一下,您将照片缩小 8 倍。您不能指望通过将缩小的 1 放大 8 倍来恢复原始照片。你只能在好莱坞电影中做到这一点。
  • @paddy 我知道这一切,我只是觉得这很奇怪,我无法在原始最小值/最大值之间进行缩放。好像我在某个地方离了 1 点。
  • @Joshua 这完全正常,因为您丢失了有关原始值的信息。请参阅我的回答,了解如何实现您的要求,以及为什么这不一定是一件好事。

标签: math division short integer-division


【解决方案1】:

已经提供了下采样的答案。

此答案与使用全范围进行上采样有关。这是一个 C99 sn-p 演示如何将错误分散到整个值范围内:

#include <stdio.h>

int main(void)
{
    for( int i = 0; i < 256; i++ ) {
        unsigned short scaledVal = ((unsigned short)i << 8) + (unsigned short)i;
        printf( "%8d%8hu\n", i, scaledVal );
    }
    return 0;
}

这很简单。您将值左移 8,然后将原始值加回。这意味着 [0,255] 范围内每增加 1 对应于 [0,65535] 范围内增加 257。

我想指出,这可能会产生比您开始时更糟糕的结果。例如,如果您对 65280 (0xff00) 进行下采样,您将得到 255,但随后进行上采样会得到 65535 (0xffff),总误差为 255。在数据范围的大部分高端,您将有类似的大错误.

您最好放弃返回 [0,65535] 范围的概念,而是将您的值四舍五入。也就是说,左移并加 127。这意味着误差是均匀的而不是倾斜的。因为您实际上并不知道原始值是多少,所以您能做的最好的就是用一个位于中心的值来估计它。

总而言之,我认为这在数学上更正确:

unsigned short scaledVal = ((unsigned short)i << 8) + 127;

【讨论】:

  • 我将实现您的“数学正确”版本。我想回到 [0-65535] 我仍然乘以 256,对吧?
  • 不,这就是重点。如果你想将 [0,255] 扩展为完全乘以 [0,65535] 然后乘以 257——这与我在代码示例中所做的相同,只是当时我没有意识到。跨度>
  • LOL 你说得对,这个问题的答案是乘以 257 而不是 256,我知道答案一定很简单 :)
【解决方案2】:

您没有得到原始最大值,因为您不能将数字 256 表示为 8 位无符号整数。

【讨论】:

  • 这不是一个真正的答案,只是一个观察。为什么在这里我可以仅用两倍的空间(16 位)代表两倍以上的可能性?
  • @Joshua 您需要了解二进制求幂。多 8 位意味着 256 倍的可能性。
  • @Joshua 我的意思是,您想知道为什么范围从 -32768 到 +32767 的有符号 16 位 int 不能直接扩展到范围从0 到 256... 答案是后者不存在。正如 paddy 在您在上面接受的答案中指出的那样,缩小然后备份本质上将是一个有损且不准确的过程。
  • @dodgethesteamroller 我完全理解这将是有损的,我什至愿意失去更多的准确性,只是为了获得最大值为 32767。我只是希望样本在快速时看起来像真正的 16 位检查,现在非常明显的是,当信号剪辑到最大值时它已被转换。也许我应该多加一行:'if(Sample == 32512) Sample = 32767;'
【解决方案3】:

如果您尝试将 16 位整数值压缩到 8 位整数值范围内,您将获取最高有效 8 位并保留它们,同时丢弃最低有效 8 位。通常这是通过移位位来完成的。 &gt;&gt; 运算符是从最高有效位到最低有效位的转换,如果使用 8 次或&gt;&gt;8 将起作用。您也可以只屏蔽字节并在除法之前将 00 分掉,例如 8BitInt = (16BitInt & 65280)/256; [65280 又名 0xFF00]

您从一个值中移出的每一位都会将其减半,例如除以 2,然后向下舍入。

由于您正在处理有符号整数,因此上述所有内容都有些复杂。

最后我不能 100% 确定我在这里得到了所有东西,因为真的,我没有尝试过这样做。

【讨论】:

  • 您可以通过将值转换为unsigned short 并添加32768U 来转换连续范围。然后你可以依靠转变。见ideone.com/hx2LK8
  • 不是压缩有什么问题,是解压;)
猜你喜欢
  • 2012-09-05
  • 2017-09-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多