Nano Hash - криптовалюты, майнинг, программирование

двоичное представление числа с плавающей запятой

почему эти два вызова функции toBinary вычисляют один и тот же результат (по крайней мере, в VS2010)?

#include <iostream>
#include <bitset>
#include <limits>
using namespace std;

template<class T> bitset<sizeof(T)*CHAR_BIT> toBinary(const T num) 
{
    bitset<sizeof(T)*CHAR_BIT> mybits;
    const char * const p = reinterpret_cast<const char*>(&num);
    for (int i = sizeof(T)*CHAR_BIT-1 ; i >= 0 ; --i)
        mybits.set(i, (*(p)&(1<<i) ));
    return mybits;
}

int main() 
{
    cout << toBinary(8.9).to_string() << "\n"; 
    cout << toBinary( 8.9 + std::numeric_limits<double>::epsilon() ).to_string()  << "\n"; 
    cin.get();
}
14.04.2011

Ответы:


1

Этот эпсилон относится к 1; здесь вместо этого вы суммируете его до 8,9, что более чем в 8 (2 ^ 3) раз больше, чем 1. Это означает, что этот эпсилон изменит двоичную цифру, которая находится на три цифры справа от самой правой цифры, хранящейся в этом двойной.

Если вы хотите, чтобы что-то изменилось, вы должны добавить около 8,9 * эпсилон.

14.04.2011
  • какое значение я должен добавить, чтобы получить наименьшее приращение, потому что, если я добавлю, например, 8,8, я также получу изменение? 15.04.2011
  • Нет единого значения, которое делает то, что вы хотите, но есть библиотечная функция — см. мой ответ. 15.04.2011
  • Хм, я тоже проверил и получил такой результат, я думаю, это зависит от точного внутреннего представления числа. Хорошее эмпирическое правило заключается в том, что number*epsilon дает вам значение, которое, добавленное к number, обязательно изменит его, в то время как при этом у вас нет гарантий. 15.04.2011

  • 2

    У вас есть две проблемы. Во-первых, ваша функция toBinary не делает то, что вы хотели - она ​​должна читаться так (при условии, что процессор с прямым порядком байтов):

    template<class T> bitset<sizeof(T)*CHAR_BIT> toBinary(const T num)
    {
        bitset<sizeof(T)*CHAR_BIT> mybits;
        const char * const p = reinterpret_cast<const char*>(&num);
        for (int i = sizeof(T)-1; i >= 0; i--)
            for (int j = CHAR_BIT-1; j >= 0; j--)
                mybits.set(i*CHAR_BIT + j, p[i] & (1 << j));
        return mybits;
    }
    

    Другая проблема заключается в том, как описывает Маттео: numeric_limits<double>::epsilon — это разница между 1.0 и следующим большим представимым значением, а не разница между любым числом с плавающей запятой и следующим большим представимым значением. Вы можете убедиться в этом сами, изменив свою программу, чтобы попытаться увеличить 0.5, 1.0 и 2.0 -- добавление epsilon увеличит последний бит 0.5, последний бит 1.0, и не влияет на 2.0.

    Однако есть способ сделать то, что вы пытаетесь сделать: семейство nextafter функций (они являются частью C99).

    14.04.2011
  • я не понимаю, почему функция ma имеет ошибку. По моему мнению, (*(p)&(1‹‹i) будет проверять каждый бит? нет? 16.04.2011
  • Нет, не будет. *p имеет тип char, поэтому он извлекает либо первые, либо последние CHAR_BIT биты вашего double (в зависимости от порядка байтов) и только эти биты. Таким образом, большая часть вашего цикла читает не бит i из double, а копию бита 7 с расширением знака. Чтобы увидеть второй и последующие байты double, вы должны сместить p перед разыменованием его , что и делает мой модифицированный цикл. 16.04.2011
  • Я не понимаю, почему это (1‹‹j), а не (1‹‹i) 16.04.2011
  • Потому что i — это индекс byte. Каждая итерация цикла должна обращаться к биту i*CHAR_BIT + j в пределах исходного значения. Если бы он использовал 1<<i, он бы не читал каждый бит. 16.04.2011
  • что для меня не естественно, так это то, что для двух разных индексов байтов (например, i = 0 и i = 8) у нас есть одна и та же маска (j = 0) ... поэтому маска не соответствует правильному положению для я =0, по крайней мере, в моей голове... 16.04.2011
  • Вы все еще думаете об этом, как если бы мы оперировали гигантским битовым полем, содержащим весь двойник. Не были. p[i] извлекает одиночный байт из двойного по смещению i; & (1 << j) извлекает бит из этого байта по смещению j. Номера битов в каждом байте всегда равны 0..(CHAR_BIT-1). 16.04.2011
  • Для справки, не существует портативного способа получить гигантское битовое поле, содержащее весь двойник. Вот как вы должны это сделать, если вы не любите гадкие трюки с союзами. 16.04.2011
  • Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

    Как написать эффективное резюме
    Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

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

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

    Понимание расстояния Вассерштейна: мощная метрика в машинном обучении
    В обширной области машинного обучения часто возникает необходимость сравнивать и измерять различия между распределениями вероятностей. Традиционные метрики расстояния, такие как евклидово..

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..