Почему numeric_limits Machine Epsilon не удовлетворяет условию 1+e>1?

Если я не ошибаюсь, определение машины Эпсилон — это наименьшее число, удовлетворяющее условию:

Почему numeric_limits Machine Epsilon не удовлетворяет условию 1+e>1?

Я пытался проверить это, используя std::numeric_limits<float>::epsilon(), но значение не удовлетворяет этому, если вы попытаетесь получить предыдущее число с плавающей запятой с помощью std::nextafter:

#include <cmath>
#include <iostream>
#include <limits>

int main() {
    float e = std::numeric_limits<float>::epsilon();
    float previous = std::nextafter(e, -std::numeric_limits<float>::infinity());

    std::cout << std::boolalpha << ((1.0f + previous) > 1.0f) << std::endl;

    return 0;
}

Этот кадр выводит truehttps://coliru.stacked-crooked.com/a/841e19dafcf0bf6f.

После попытки получить число с помощью std::nextafter я заметил, что правильный Machine Epsilon должен быть:

std::nextafter(std::numeric_limits<float>::epsilon() / 2.0f, std::numeric_limits<float>::infinity())

Я протестировал его, используя этот код:

#include <cmath>
#include <iostream>
#include <limits>

bool verify(float e) {
    return ((1.0f + e) > 1.0f);
}

int main() {
    std::cout.precision(std::numeric_limits<float>::digits);
    std::cout << std::boolalpha << std::fixed;

    float epsilon = std::numeric_limits<float>::epsilon();

    float last = epsilon;
    while (true) {
        last = std::nextafter(last, -std::numeric_limits<float>::infinity());
        if ((1.0f + last) > 1.0f) {
            epsilon = last;
        } else {
            break;
        }
    }

    // Does not satisfy condition
    std::cout << "last: " << verify(last) << " " << last << std::endl;
    // Satisfy condition
    std::cout << "epsilon: " << verify(epsilon) << " " << epsilon << std::endl;

    float half_epsilon = std::numeric_limits<float>::epsilon() / 2.0f;
    float actual_epsilon = std::nextafter(half_epsilon, std::numeric_limits<float>::infinity());
    // Same as 'last' at this point
    std::cout << "half_epsilon: " << verify(half_epsilon) << " " << half_epsilon << std::endl;
    // Same as 'epsilon' at this point
    std::cout << "actual_epsilon: " << verify(actual_epsilon) << " " << actual_epsilon << std::endl;

    return 0;
}

Это выводит

last: false 0.000000059604644775390625
epsilon: true 0.000000059604651880817983
half_epsilon: false 0.000000059604644775390625
actual_epsilon: true 0.000000059604651880817983

https://coliru.stacked-crooked.com/a/3c66a2144e80a91b

Я что-то пропустил здесь?

stackoverflow.com/questions/28357415/… спрашивает, почему double и float epsilon считаются одинаковыми и почему это так.
1201ProgramAlarm 24.12.2020 03:47
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
1
211
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Если я не ошибаюсь, определение Машинного Эпсилона — это наименьшее число, удовлетворяющее условию: [1 + epsilon > 1]

Близко, но вы ошибаетесь в контексте C++. (Я считаю, что ваше определение правильно в других, более академических контекстах.) Согласно cppreference.com, машинный эпсилон - это «разница между 1.0 и следующим значением, представленным [указанным] типом с плавающей запятой». Эпсилон машины действительно удовлетворяет 1 + epsilon > 1, но это не обязательно должно быть наименьшее число, удовлетворяющее этому. Однако это наименьшее число, удовлетворяющее этому условию при всех режимах округления.

Поскольку машинный эпсилон намного меньше, чем 1.0, между эпсилон и 0.0 существует множество представляемых значений. (Это основная цель представлений с плавающей запятой.) Когда любое из них добавляется к 1.0, результат не может быть представлен, поэтому результат необходимо округлить. Если режим округления — до ближайшего представимого значения, то эта сумма будет округляться до 1 + epsilon всякий раз, когда малое число находится между epsilon/2 и 3*epsilon/2. С другой стороны, если режим округления всегда направлен к нулю, вы получите ожидаемый результат.

Попробуйте добавить #include <cfenv> и следующую строку в свой код.

fesetround(FE_TOWARDZERO);

Это заставляет любую сумму строго между 1.0 и 1 + epsilon округляться до 1.0. Теперь вы должны увидеть, что машина epsilon ведет себя так, как вы ожидали.

Другие гарантированные режимы округления — в сторону -бесконечности и в сторону +бесконечности. Подробности смотрите на cppreference.com.

Другие вопросы по теме