Допустим, вы пишете программу для обработки изображений. Программа получает изображение, преобразует его в значения с плавающей запятой, выполняет обработку и сохраняет изменённые пиксели на диск в виде 8-битных цветов. Сегодня я хочу рассмотреть вопрос преобразования целых значений в значения с плавающей запятой. Существует два решения, которые на Python и NumPy выглядят так:Стандартное деление на 255pixels = img / 255.0result = process(pixels)output = np.trunc(result * 255 + 0.5)Альтернативное деление на 256pixels = (img + 0.5) / 256.0result = process(pixels)output = np.trunc(result * 256)Я предполагаю, что в обоих случаях выходные значения ограничиваются перед окончательным преобразованием типов:# Ограничение и преобразование в 8 битoutput_8bit = output.clip(0, 255).astype(np.uint8)В стандартном случае целочисленный 0 соответствует 0.0, а 255 соответствует 1.0. Это работает абсолютно нормально и именно так всё реализовано в GPU. В альтернативном случае прибавляется смещение на 0,5 и деление происходит на 256, поэтому целочисленный 0 соответствует 0.5/256=0.001953125. Это неудобно, потому что код обработки изображений, например, без знания константы не сможет обнаруживать чёрные пиксели. Из‑за этого мы привязываем логику к 8-битным значениям, даже если вычисления выполняются с плавающей запятой. В стандартном решении всегда можно предполагать, что чёрный соответствует 0.0.Однако некоторых программистов всё равно притягивает альтернативное решение. В чём дело? Чем оно им так нравится? Читать далее