// 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的分配?您可以通过执行两个分区轻松地将其分解(减少四舍五入)为两个uint16_t的添加.你能用uint8_t做到吗?
符合标准的C实现保证执行至少16位的算术运算.C standard第6.3.1.1p2节规定:
The following may be used in an expression wherever an
int
orunsigned
may be used:
int
- An object or expression with an integer type (other than
int
orunsigned int
) whose integer conversion rank is less than
or equal to the rank ofint
andunsigned int
.- A bit-field of type
_Bool
,int
,signed int
,orunsigned int
.If an
int
can represent all values of the original type (as
restricted by the width, for a bit-field), the value is
converted to anint
; otherwise, it is converted to anunsigned
. These are called the integer promotions. All other
int
types are unchanged by the integer promotions.
第E.1节还规定int必须能够支持至少在-32767到32767范围内的值,而unsigned int必须支持至少在0到65535范围内的值.
由于uint8_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以强制将值转换为unsigned int按照6.3.1.8节中规定的通常算术转换:
the integer promotions are performed on both operands. Then the
following rules are applied to the promoted operands:
- If both operands have the same type, then no further conversion is needed.
- Otherwise, if both operands have signed integer types or both have unsigned integer types, the operand with the type of lesser
integer conversion rank is converted to the type of the operand
with greater rank.- Otherwise, if the operand that has unsigned integer type has rank greater or equal to the rank of the type of the
other operand, then the operand with signed integer type is
converted to the type of the operand with unsigned integer
type.
另请注意,我们将每个部分分别除以总和.如果我们在分割之前首先添加两个部分,则分子可能超过65535.通过首先进行除法,这会使每个子表达式回到uint8_t的范围内.然后我们可以添加两个部分,它们将再次位于uint8_t的范围内.
因此,上述表达式保证在符合C标准的编译器上返回正确的精确答案.