เหตุใด numeric_limits Machine Epsilon จึงไม่เป็นไปตามเงื่อนไข 1 + e> 1

Dec 24 2020

ถ้าฉันไม่ผิดคำจำกัดความของ Machine Epsilon คือตัวเลขต่ำสุดที่ตรงตามเงื่อนไข:

ฉันพยายามทดสอบการใช้ประโยชน์จากสิ่งนี้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;
}

เอาต์พุตภาพนิ่งนี้ true https://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

ฉันคิดถึงอะไรบางอย่างที่นี่หรือเปล่า?

คำตอบ

6 JaMiT Dec 24 2020 at 11:46

ถ้าฉันไม่ผิดคำจำกัดความของ Machine Epsilon คือตัวเลขต่ำสุดที่ตรงตามเงื่อนไข: [ 1 + epsilon > 1]

ปิด แต่คุณคิดผิดในบริบทของ C ++ (ฉันเชื่อว่าคำจำกัดความของคุณถูกต้องในบริบททางวิชาการอื่น ๆ ) จากข้อมูลของcppreference.comเครื่อง epsilon คือ "ความแตกต่างระหว่าง1.0และค่าถัดไปที่แสดงได้โดยประเภททศนิยม [ระบุ]" เครื่อง epsilon ตอบสนอง1 + epsilon > 1ได้ แต่ไม่จำเป็นต้องเป็นตัวเลขต่ำสุดที่ตรงตามนั้น มันเป็นแม้ว่าจำนวนต่ำสุดที่น่าพอใจว่าเงื่อนไขภายใต้โหมดการปัดเศษทั้งหมด

เนื่องจากเครื่อง epsilon มีขนาดเล็กกว่ามากจึง1.0มีค่าที่แสดงได้มากมายระหว่าง epsilon และ0.0. (นั่นเป็นเป้าหมายพื้นฐานของการแสดงจุดลอยตัว) เมื่อเพิ่มสิ่งเหล่านี้เข้าไป1.0ผลลัพธ์จะไม่สามารถแสดงได้ดังนั้นจึงต้องปัดเศษผลลัพธ์ หากโหมดการปัดเศษเป็นค่าที่ใกล้ที่สุดแทนได้แล้วว่าผลรวมจะปัดเศษ1 + epsilonเมื่อใดก็ตามที่จำนวนน้อยอยู่ระหว่างและepsilon/2 3*epsilon/2ในทางกลับกันถ้าโหมดการปัดเศษเป็นศูนย์เสมอคุณจะได้ผลลัพธ์ที่คุณคาดหวัง

ลองเพิ่ม#include <cfenv>และบรรทัดต่อไปนี้ในรหัสของคุณ

fesetround(FE_TOWARDZERO);

นี้ทำให้เกิดผลรวมใดอย่างเคร่งครัดระหว่าง1.0และรอบการ1 + epsilon 1.0ตอนนี้คุณควรเห็นเครื่อง epsilon ทำงานตามที่คุณคาดไว้

โหมดการปัดเศษที่รับประกันอื่น ๆ คือไปทาง -infinity และไปยัง + infinity ดูรายละเอียดได้ที่cppreference.com