Как написать функцию std :: floor с нуля - PullRequest
2 голосов
/ 17 сентября 2011

Я хотел бы знать, как написать свою собственную функцию этажа для округления числа с плавающей точкой.

Можно ли сделать это, установив биты с плавающей точкой, которые представляют числа после запятой, равными 0?

Если да, то как я могу получить доступ и изменить эти биты?

Спасибо.

Ответы [ 4 ]

4 голосов
/ 17 сентября 2011

Вы можете выполнить битовую перестановку чисел с плавающей запятой, но правильное понимание этого зависит от точного знания двоичного представления с плавающей запятой. Для большинства машин в наши дни это IEEE-754 , что достаточно просто. Например, 32-разрядные числа с плавающей запятой IEEE-754 имеют 1 знаковый бит, 8 битов экспоненты и 23 бита мантиссы, поэтому вы можете использовать сдвиги и маски для извлечения этих полей и выполнения действий с ними. Таким образом, выполнить trunc (округление до целого в 0) довольно просто:

float trunc(float x) {
    union {
        float    f;
        uint32_t i;
    } val;
    val.f = x;
    int exponent = (val.i >> 23) & 0xff; // extract the exponent field;
    int fractional_bits = 127 + 23 - exponent;
    if (fractional_bits > 23) // abs(x) < 1.0
        return 0.0;
    if (fractional_bits > 0)
        val.i &= ~((1U << fractional_bits) - 1);
    return val.f;
}

Сначала мы извлекаем поле экспоненты и используем его для вычисления количества битов после десятичная точка присутствует в числе. Если размер мантиссы превышает размер, мы просто возвращаем 0. В противном случае, если есть хотя бы 1, мы маскируем (очищаем) столько младших битов. Довольно просто Мы игнорируем denormal, NaN и бесконечность ее, но это работает нормально, так как они имеют показатели всех 0 или всех 1, что означает, что мы в конечном итоге преобразуем денорм в 0 (они попадают в первую очередь, если, наряду с маленькими нормальные числа) и оставив NaN / Inf без изменений.

Чтобы взять слово, вам также нужно взглянуть на знак и округлить отрицательные числа «вверх» в сторону отрицательной бесконечности.

Обратите внимание, что это почти наверняка медленнее, чем использование выделенных операций с плавающей запятой, поэтому подобные вещи действительно полезны, только если вам нужно использовать числа с плавающей запятой на оборудовании, которое не имеет встроенной поддержки с плавающей запятой. Или если вы просто хотите поиграть и узнать, как эти вещи работают на низком уровне.

3 голосов
/ 17 сентября 2011

Определить с нуля. И нет, установка битов числа с плавающей запятой, представляющего числа после запятой в 0, не будет работать. Если вы посмотрите на IEEE-754 , вы увидите, что у вас в основном все числа с плавающей запятой в форме:

0.xyzxyzxyz 2^(abc)

Таким образом, чтобы реализовать настил, вы можете получить xyzxyzxyz и сдвинуть влево на abc + 1 раз. Оставь остальные. Я предлагаю вам прочитать двоичное представление числа с плавающей запятой (ссылка выше), это должно пролить свет на предложенное мной решение.

ПРИМЕЧАНИЕ. Вам также необходимо позаботиться о знаковом бите. И мантисса вашего номера отключена на 127.

Вот пример. Допустим, у вас есть число pi: 3,14 ..., вы хотите получить 3.

Pi представлен в двоичном виде как

0 10000000 10010010000111111011011

Это переводится на

sign = 0 ; e = 1 ; s = 110010010000111111011011

Выше я получаю прямо из Википедии . Поскольку е равно 1. Вам нужно сдвинуть влево s на 1 + 1 = 2, поэтому вы получите 11 => 3.

0 голосов
/ 02 февраля 2019

Кастинг без знака при возврате в виде двойного делает то, что вы ищете, но под капотомЭтот простой кусок кода работает для любого ПОЗИТИВНОГО номера.

#include <iostream>

double floor(const double& num) {
    return (unsigned long long) num; 
}
0 голосов
/ 17 сентября 2011
#include <iostream>
#include <iomanip>

double round(double input, double roundto) {
    return int(input / roundto) * roundto;
}

int main() {
    double pi = 3.1415926353898;
    double almostpi = round(pi, 0.0001);
    std::cout << std::setprecision(14) << pi << '\n' << std::setprecision(14) << almostpi;
}

http://ideone.com/mdqFA вывод:

+3,1415926353898
3,1415

Это будет намного быстрее, чем любой небольшой поворот, который вы можете придумать. И он работает на всех компьютерах (с плавающей точкой) вместо одного типа.

...