簡體   English   中英

浮點數和雙精度數之間 output 值 (+/-) 的細微差別

[英]Subtle differences in output values (+/-) between float and doubles

這是對此處發現的一個較舊問題的跟進: Chaining Function Calls和用戶Mooing Duck為我提供了一個通過使用代理 Class 和代理功能的答案。 我已經成功地制作了這個 class 的模板,它似乎正在工作。 我在floatdouble之間得到完全不同的結果......

以下是floatsdoubles應用程序的非模板版本:

只需將函數代理函數中的所有float s 替換為double s ... 除了arguments之外,主程序不會改變。

#include <cmath>
#include <exception>
#include <iostream>
#include <utility>

namespace pipes {
          
    const double PI = 4 * atan(1);

    struct vec2 {
        float x;
        float y;
    };

    std::ostream& operator<<(std::ostream& out, vec2 v2) {
        return out << v2.x << ',' << v2.y;
    }
 
    vec2 translate(vec2 in, float a) {
        return vec2{ in.x + a, in.y + a };
    }

    vec2 rotate(vec2 in, float a) {
        // convert a in degrees to radians:
        a *= (float)(PI / 180.0);
        return vec2{ in.x*cos(a) - in.y*sin(a),
            in.x*sin(a) + in.y*cos(a) };
    }

    vec2 scale(vec2 in, float a) {
        return vec2{ in.x*a, in.y*a };
    }    

    // proxy class
    template<class rhst, vec2(*f)(vec2, rhst)>
    class vec2_op1 {
        std::decay_t<rhst> rhs; // store the parameter until the call
    public:
        vec2_op1(rhst rhs_) : rhs(std::forward<rhst>(rhs_)) {}
        vec2 operator()(vec2 lhs) { return f(lhs, std::forward<rhst>(rhs)); }
    };

    // proxy methods      
    vec2_op1<float, translate> translate(float a) { return { a }; }
    vec2_op1<float, rotate> rotate(float a) { return { a }; }
    vec2_op1<float, scale> scale(float a) { return { a }; }

    // lhs is the object, rhs is the operation on the object
    template<class rhst, vec2(*f)(vec2, rhst)>
    vec2& operator|(vec2& lhs, vec2_op1<rhst, f>&& op) { return lhs = op(lhs); }

} // namespace pipes

int main() {
    try {
        pipes::vec2 a{ 1.0, 0.0 };
        pipes::vec2 b = (a | pipes::rotate(90.0));
        std::cout << b << '\n';
    } catch (const std::exception& e) {
    std::cerr << e.what() << "\n\n";
    return EXIT_FAILURE;
}
return EXIT_SUCCESS;

Output 用於浮動:

-4.37114e-08,1

Output 雙:

6.12323e-17,1

這是模板版本...

#include <cmath>
#include <exception>
#include <iostream>
#include <utility>

namespace pipes {
          
    const double PI = 4 * atan(1);

    template<typename Ty>
    struct vec2_t {
        Ty x;
        Ty y;
    };

    template<typename Ty>
    std::ostream& operator<<(std::ostream& out, vec2_t<Ty> v2) {
        return out << v2.x << ',' << v2.y;
    }

    template<typename Ty>
    vec2_t<Ty> translate(vec2_t<Ty> in, Ty a) {
        return vec2_t<Ty>{ in.x + a, in.y + a };
    }

    template<typename Ty>
    vec2_t<Ty> rotate(vec2_t<Ty> in, Ty a) {
        // convert a in degrees to radians:
        a *= (Ty)(PI / 180.0);
        return vec2_t<Ty>{ in.x*cos(a) - in.y*sin(a),
                     in.x*sin(a) + in.y*cos(a) };
    }

    template<typename Ty>
    vec2_t<Ty> scale(vec2_t<Ty> in, Ty a) {
        return vec2_t<Ty>{ in.x*a, in.y*a };
    }

    // proxy class
    template<class rhst, typename Ty, vec2_t<Ty>(*f)(vec2_t<Ty>, rhst)>
    class vec2_op1 {
        std::decay_t<rhst> rhs; // store the parameter until the call
    public:
        vec2_op1(rhst rhs_) : rhs(std::forward<rhst>(rhs_)) {}
        vec2_t<Ty> operator()(vec2_t<Ty> lhs) { return f(lhs, std::forward<rhst>(rhs)); }
    };

    // proxy methods
    template<typename Ty>
    vec2_op1<Ty, Ty, translate<Ty>> translate(Ty a) { return { a }; }
    template<typename Ty>
    vec2_op1<Ty, Ty, rotate<Ty>> rotate(Ty a) { return { a }; }
    template<typename Ty>
    vec2_op1<Ty, Ty, scale<Ty>> scale(Ty a) { return { a }; }

    // overloaded | operator for chaining function calls to vec2_t objects
    // lhs is the object, rhs is the operation on the object
    template<class rhst, typename Ty, vec2_t<Ty>(*f)(vec2_t<Ty>, rhst)>
    vec2_t<Ty>& operator|(vec2_t<Ty>& lhs, vec2_op1<rhst, Ty, f>&& op) { return lhs = op(lhs); }

} // namespace pipes

// for double just instantiate with double... 
int main() {
    try {    
        pipes::vec2_t<float> a{ 1.0f, 0.0f };
        pipes::vec2_t<float> b = (a | pipes::rotate(90.0f));    
        std::cout << b << '\n';    
    } catch (const std::exception& e) {
        std::cerr << e.what() << "\n\n";
        return EXIT_FAILURE;
    }
    return EXIT_SUCCESS;
}

用於浮動的 output:

-4.37114e-08,1

output 雙打:

6.12323e-17,1

這表明我的 class 到 class 模板的轉換似乎正在工作。 我知道由於在轉換時從double轉換為float或從float擴大到double可能會丟失一些精度,但是,我似乎無法理解為什么 output 值與一個有如此大的差異對另一個...

點或向量{1,0}旋轉 90 度或 PI/2 弧度應為{0,1} 我了解浮點運算的工作原理,並且為x值生成的 output 相對接近0 ,因此對於所有時態和目的,它們都應被視為0 ,我可以包括使用epsilon檢查 function 以測試它是否足夠接近到0直接將其設置為0這不是問題...

讓我好奇的是,為什么浮點數是-4.3...e-8而雙倍是+6.1...e-17 在 float 情況下,我得到負值,而對於 double 情況,我得到正值。 在這兩種情況下,是的,它們都非常小並且接近於 0,這很好,但是相反的跡象,這讓我摸不着頭腦?

我正在尋求清晰以更好地了解為什么這些值會以它們的方式生成......它來自類型轉換還是由於正在使用的觸發 function? 還是兩者兼而有之? 只是試圖查明跡象的分歧來自哪里......

我需要知道是什么導致了這種細微的差異,因為它與我對這個 class 及其生成的輸出的使用有關,當精度優於足夠好的估計時。



編輯

在使用這些 function 模板的實例化時,特別是對於旋轉 function,我開始為我的矢量對象測試<int>類型......我開始遇到一些編譯器錯誤......翻譯和縮放功能很好,我由於loss of datanarrowingwidening轉換等類似原因,旋轉 function 僅存在問題...

我不得不將我的旋轉功能的實現更改為:

template<typename Ty>
vec2_t<Ty> rotate(vec2_t<Ty> in, Ty a) {
    // convert a in degrees to radians:
    auto angle = (double)(a * (PI / 180.0));
    return vec2_t<Ty>{ static_cast<Ty>( in.x*cos(angle) - in.y*sin(angle) ),
                       static_cast<Ty>( in.x*sin(angle) + in.y*cos(angle) ) 
                     };
}

在這里,無論Ty類型如何,我都強制角度始終為double 旋轉 function 仍然期望其參數的類型與正在實例化的vec2_t object 的類型相同。 問題在於正在創建並從計算返回的vec2_t object 的初始化。 我必須將xy坐標顯式地static_cast轉換為Ty 現在,當我為vec2_t<int>嘗試上述相同的程序時,傳入90的旋轉值,我得到的 output 正好是0,1

另一個有趣的事實是,通過強制角度始終為double並始終將計算值轉換回Ty ,當我將vec2_t實例化為doublefloat時,我總是得到正的6.123...e-17結果案例...這也應該允許我簡化is_zero() function 的設計,以測試這些值是否足夠接近0以將它們顯式設置為0

TL;DR:無論符號如何,小數字都接近於零。 在這種情況下,你得到的數字“幾乎為零”。

我稱之為“標志痴迷”。 兩個非常小的數字是相似的,即使它們的符號不同。 在這里,您正在查看您執行的計算精度邊緣的數字。 考慮到它們的類型,它們都同樣“小”。 其他答案提示了錯誤的確切位置:)

你的問題是:

    a *= (Ty)(PI / 180.0);

對於float情況,計算結果為 1.570796371

對於double情況,計算結果為 1.570796327

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM