Есть ли способ приблизиться к соответствующим возвращаемым значениям, не требуя каких-либо выделений, превышающих 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];
}
На практикеэто будет стоить 4 ГБ ОЗУ для справочной таблицы, так что это, вероятно, не очень хорошая идея.
Кроме того, это зависит от того, что вы подразумеваете под «близко» (насколько велика «допустимая ошибка в худшем случае»)может быть) и какой диапазон значений действителен (особенно для parts_x
и parts_y
).
Например (если parts_x
и parts_y
имеют диапазон только от 1 до 15):
uint8_t blend(uint8_t x, uint8_t y, uint8_t parts_x, uint8_t parts_y) {
uint8_t scaleX = (parts_x << 4) / (parts_x + parts_y);
uint8_t scaleY = (parts_y << 4) / (parts_x + parts_y);
return (x >> 4) * scaleX + (y >> 4) * scaleY;
}
Конечно, в этом случае «закрыть» означает:
- blend (100, 200, 1, 1) = 6 * 8 + 12 * 8 = 144 (не 150)
- blend (100, 200, 2, 1) = 6 * 10 + 12 * 5 = 120 (не 133)
Обратите внимание, что (в общем) умножение "расширяется».Я имею в виду, что если a
имеет M битов диапазона, а b
имеет N битов диапазона, то a*b
будет иметь M + N битов диапазона.Другими словами (используя полный диапазон), чтобы избежать переполнения uint8_t * uint8_t = uint16_t
.Деление значительно хуже (например, чтобы избежать потери точности, 1/3 требует бесконечных битов), некоторые потери точности невозможно избежать, количество битов в результате определяет, сколько потери точности, а 8 бит точности "не много".
Также обратите внимание, что простой пример, который я показал выше, может быть улучшен в некоторых случаях, добавив дополнительный код для этих случаев.Например:
uint8_t blend(uint8_t x, uint8_t y, uint8_t parts_x, uint8_t parts_y) {
if(parts_x < parts_y) {
return blend(y, x, parts_y, parts_x);
}
// parts_x <= parts_y now
if(parts_x == parts_y*2) {
return 2*(x/3) + y/3;
} else if(parts_x == parts_y*3) {
return 3*(x/4) + y/4;
} else if(parts_x == parts_y*4) {
return 4*(x/5) + y/5;
} else if(parts_x == parts_y*5) {
return 5*(x/6) + y/6;
} else if( (x > 16) && (y > 16) ){
uint8_t scaleX = (parts_x << 4) / (parts_x + parts_y);
uint8_t scaleY = (parts_y << 4) / (parts_x + parts_y);
return (x * scaleX + y * scaleY) >> 4;
} else {
uint8_t scaleX = (parts_x << 4) / (parts_x + parts_y);
uint8_t scaleY = (parts_y << 4) / (parts_x + parts_y);
return (x >> 4) * scaleX + (y >> 4) * scaleY;
}
}
Конечно, значительно проще и быстрее использовать что-то большее, чем uint8_t
, поэтому ...