Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/65.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_Algorithm_Integer Overflow - Fatal编程技术网

C 如何";混合;两个值没有溢出?

C 如何";混合;两个值没有溢出?,c,algorithm,integer-overflow,C,Algorithm,Integer Overflow,考虑以下功能: // Return a blended value of x and y: // blend(100, 200, 1, 1) -> 150 // blend(100, 200, 2, 1) -> 133 uint8_t blend(uint8_t x, uint8_t y, uint8_t parts_x, uint8_t parts_y) { uint32_t big_parts_x = parts_x; uint32_t big_parts_y =

考虑以下功能:

// Return a blended value of x and y:
//   blend(100, 200, 1, 1) -> 150
//   blend(100, 200, 2, 1) -> 133
uint8_t blend(uint8_t x, uint8_t y, uint8_t parts_x, uint8_t parts_y) {
  uint32_t big_parts_x = parts_x;
  uint32_t big_parts_y = parts_y;
  return (uint8_t) ((big_parts_x * x + big_parts_y * y) /
                    (big_parts_x + big_parts_y));
}

有没有一种方法可以在不需要任何大于
uint8\t
的分配的情况下接近适当的返回值?通过执行两个除法,您可以轻松地将其分解(较少舍入)为两个加法。你能只用
uint8\t
来做吗

是否有一种方法可以在不需要任何大于uint8\t的分配的情况下接近适当的返回值

理论上,是的:

uint8_t blend(uint8_t x, uint8_t y, uint8_t parts_x, uint8_t parts_y) {
  return lookup_table[x][y][parts_x][parts_y];
}
实际上,查找表需要花费4gib的内存,所以这可能不是一个好主意

除此之外,它还取决于“关闭”的含义(“可接受的最坏情况错误”有多大)以及有效的值范围(尤其是对于
零件x
零件y

例如(如果
零件x
零件y
的范围仅为1到15):


当然,使用比
uint8\t
更大的东西要容易得多,也要快得多,因此……

下面的内容将在不进行任何额外分配的情况下合并。
即使
int/unsigned
为16位也可以工作

return (uint8_t) ((1u*parts_x*x + 1u*parts_y*y) / (0u + parts_x + parts_y));

符合标准的C实现保证执行至少16位的算术运算

第6.3.1.1p2节规定:

如果
int
无符号,则可以在表达式中使用以下内容
int
可用于:

  • 整数转换秩小于的整数类型(除
    int
    无符号int
    外)的对象或表达式 或等于
    int
    无符号int
    的秩
  • 类型的位字段
    \u Bool
    int
    signed int
    unsigned int
如果
int
可以表示原始类型的所有值(如 受宽度限制,对于位字段),值为 转换为
int
;否则,它将转换为无符号的

int
。这些被称为整数促销。所有其他 整型促销不会改变类型

第E.1节还规定,
int
必须能够支持至少在-32767到32767范围内的值,
无符号int
必须支持至少在0到65535范围内的值

由于
uint8\u t
的秩低于
int
,因此在大多数操作员使用
+
-
*
/
时,前者总是会被提升为后者

有鉴于此,您可以通过以下细微修改安全地计算该值:

uint8_t blend(uint8_t x, uint8_t y, uint8_t parts_x, uint8_t parts_y) {
    return ((1u*parts_x*x) / (parts_x + parts_y)) + ((1u*parts_y*y) / (parts_x + parts_y));
}
表达式
parts_x*x
parts_y*y
的最大值为65025。这对于16位
int
而不是16位
无符号int
来说太大了,因此每个值都乘以
1u
,以强制按照第6.3.1.8节规定的常规算术转换将值转换为
无符号int

对两个操作数执行整数提升。然后 以下规则应用于提升的操作数:

  • 如果两个操作数的类型相同,则无需进一步转换
  • 否则,如果两个操作数都具有有符号整数类型或都具有无符号整数类型,则类型为较小的操作数 整数转换秩转换为操作数的类型 具有更高的等级
  • 否则,如果具有无符号整数类型的操作数的秩大于或等于 其他操作数,则有符号整数类型的操作数为 已转换为具有无符号整数的操作数类型 类型。
还要注意的是,我们将每个部分分别除以总和。如果我们在除法之前先加上两部分,分子可能会超过65535。通过先进行除法,这会使每个子支出回到
uint8\t
的范围内。然后我们可以添加这两个部分,这两个部分将再次位于a
uint8\t
的范围内


因此,上述表达式保证在符合C标准的编译器上返回正确的精确答案。

在完成数学运算之前,所有操作数都将提升为
int
,如果
int
至少为4个字节,您的计算将不会溢出,因此我不会担心。不幸的是,对于我的目标体系结构,我只有8位的操作,这就是问题所在。我应该将其表述为“不需要对uint8_t以外的类型进行任何操作”。“对我的目标体系结构进行8位操作”并没有将C代码限制为仅8位整数数学。经典示例:第一台PC使用8位处理器,缺少16位或更宽的数学运算。@Brendan代码不使用int,而是无符号。OP特别是指“需要任何拨款”。这里没有这样的中间对象。任何数学运算都会将操作数提升为
无符号/int
,如下所示为
无符号
。因此,除非OP不想使用任何操作数,
+、-、*、/、%、&、^、|、| |、&&&、[]
等,否则转换为
无符号/int
必须是答案的一部分。您的代码使用的是
无符号int
,它肯定大于8位。我会假设(为了让问题变得有意义),代码是为某种8位微控制器编译的,这种微控制器除了8位整数外不支持任何东西。@Brendan不支持语义。C指定使用至少16位数学(通常是整数编程的一部分)进行2
uint8_t
的加法(减法、加法等),如
x+y
return (uint8_t) ((1u*parts_x*x + 1u*parts_y*y) / (0u + parts_x + parts_y));
uint8_t blend(uint8_t x, uint8_t y, uint8_t parts_x, uint8_t parts_y) {
    return ((1u*parts_x*x) / (parts_x + parts_y)) + ((1u*parts_y*y) / (parts_x + parts_y));
}