Что касается того, как начать, подумайте, как бы вы округлили (положительное) число с плавающей точкой до ближайшего целого числа. Приведение float к int усекает его. Добавление 0.5 к (положительному) числу с плавающей точкой будет увеличивать целую часть точно, когда мы хотим округлить (когда десятичная часть> = 0.5). Это дает следующее:
double round(double x) {
return (long long)(x + 0.5);
}
Чтобы добавить поддержку для параметра точности, обратите внимание, что (например, round(123456.789, -3)
) добавление 500 и усечение в месте с тысячами, по сути, равнозначно добавлению 0,5 и округлению до ближайшего целого числа, просто десятичная точка равна в другом положении. Чтобы переместить основную точку вокруг, нам нужны операции сдвига влево и вправо, которые эквивалентны умножению на основание, увеличенное до величины сдвига. То есть 0x1234 >> 3
совпадает с 0x1234 / 2**3
и 0x1234 * 2**-3
в базе 2. В базе 10:
123456.789 >> 3 == 123456.789 / 10**3 == 123456.789 * 10**-3 == 123.456789
Для round(123456.789, -3)
это означает, что мы можем выполнить вышеупомянутое умножение, чтобы переместить десятичную точку, добавить 0,5, усечь, а затем выполнить обратное умножение, чтобы переместить десятичную точку назад.
double round(double x, double p) {
return ((long long)((x * pow10(p))) + 0.5) * pow10(-p);
}
Округление путем добавления 0,5 и усечения работает нормально для неотрицательных чисел, но округляет неправильный путь для отрицательных чисел. Есть несколько решений. Если у вас есть эффективная функция sign()
(которая возвращает -1, 0 или 1, в зависимости от того, является ли число <0, == 0 или> 0 соответственно), вы можете:
double round(double x, double p) {
return ((long long)((x * pow10(p))) + sign(x) * 0.5) * pow10(-p);
}
Если нет, то есть:
double round(double x, double p) {
if (x<0)
return - round(-x, p);
return ((long long)((x * pow10(p))) + 0.5) * pow10(-p);
}