Quantcast
Channel: プログラミング
Viewing all articles
Browse latest Browse all 7910

ニアイコールを書くのが面倒くさかったから作ったクラス - ゲーム作りは楽しい

$
0
0

はじめに

浮動小数点数型を使っていると、ニアイコールの判定をしたいことが多いですが、それを書くのが面倒くさかったので
ラッパークラスを作りました。

普通はこういう関数とか作っちゃえば十分なんだけどね

if (NearlyEquals(a, b, epsilon))  {
    // Something
}

operatorとか使えると読みやすかったりもして
まぁ、小ネタです

実装

NearlyDouble.hpp

#pragma once#include <cmath>#include <iostream>struct NearlyDouble
{
    inlinestaticconstexprdouble Epsilon = 0.000001;

    staticconstexprboolEquals(double a, double b, double e = Epsilon)
    {
        // C++23でabsのconstexprは対応される// return std::abs(a - b) <= e;return (a >= b ? a - b : b - a) <= e;
    }

    double value;
    double e;

    explicitconstexprNearlyDouble(double v_, double e_ = Epsilon)
    {
        value = v_;
        e = e_;
    }

    constexpr NearlyDouble operator()(double e_)
    {
        return NearlyDouble{ value, e_ };
    }

    friendconstexpr NearlyDouble operator +(const NearlyDouble& a, const NearlyDouble& b)
    {
        return NearlyDouble{ a.value + b.value, std::max(a.e, b.e)};
    }
    friendconstexpr NearlyDouble operator +(const NearlyDouble& a, double b)
    {
        return NearlyDouble{ a.value + b, a.e };
    }
    friendconstexpr NearlyDouble operator +(double a, const NearlyDouble& b)
    {
        return NearlyDouble{ a + b.value, b.e };
    }

    friendconstexpr NearlyDouble operator -(const NearlyDouble& a, const NearlyDouble& b)
    {
        return NearlyDouble{ a.value - b.value, std::max(a.e, b.e) };
    }
    friendconstexpr NearlyDouble operator -(const NearlyDouble& a, double b)
    {
        return NearlyDouble{ a.value - b, a.e };
    }
    friendconstexpr NearlyDouble operator -(double a, const NearlyDouble& b)
    {
        return NearlyDouble{ a - b.value, b.e };
    }

    friendconstexpr NearlyDouble operator *(const NearlyDouble& a, const NearlyDouble& b)
    {
        return NearlyDouble{ a.value * b.value, std::max(a.e, b.e) };
    }
    friendconstexpr NearlyDouble operator *(const NearlyDouble& a, double b)
    {
        return NearlyDouble{ a.value * b, a.e };
    }
    friendconstexpr NearlyDouble operator *(double a, const NearlyDouble& b)
    {
        return NearlyDouble{ a * b.value, b.e };
    }

    friendconstexpr NearlyDouble operator /(const NearlyDouble& a, const NearlyDouble& b)
    {
        return NearlyDouble{ a.value / b.value, std::max(a.e, b.e) };
    }
    friendconstexpr NearlyDouble operator /(const NearlyDouble& a, double b)
    {
        return NearlyDouble{ a.value / b, a.e };
    }
    friendconstexpr NearlyDouble operator /(double a, const NearlyDouble& b)
    {
        return NearlyDouble{ a / b.value, b.e };
    }

    friendconstexprbooloperator == (const NearlyDouble& a, const NearlyDouble& b)
    {
        returnEquals(a.value, b.value, a.e + b.e);
    }
    friendconstexprbooloperator == (const NearlyDouble& a, double b)
    {
        returnEquals(a.value, b, a.e);
    }
    friendconstexprbooloperator == (double a, const NearlyDouble& b)
    {
        returnEquals(a, b.value, b.e);
    }
    friendconstexprbooloperator != (const NearlyDouble& a, const NearlyDouble& b)
    {
        return !Equals(a.value, b.value, a.e + b.e);
    }
    friendconstexprbooloperator != (const NearlyDouble& a, double b)
    {
        return !Equals(a.value, b, a.e);
    }
    friendconstexprbooloperator != (double a, const NearlyDouble& b)
    {
        return !Equals(a, b.value, b.e);
    }

    template<class CharType>
    friendstd::basic_ostream<CharType>& operator<<(std::basic_ostream<CharType>& output, const NearlyDouble& value)
    {
        return output << value.value;
    }
};

constexpr NearlyDouble operator""_nearly(longdouble v)
{
    return NearlyDouble{ static_cast<double>(v)};
}
constexpr NearlyDouble operator""_nearly(unsignedlonglong v)
{
    return NearlyDouble{ static_cast<double>(v)};
}

使い方

ユーザー定義リテラルで作れる

#include "NearlyDouble.hpp"intmain()
{
    static_assert(0.1 + 0.2 != 0.3);
    static_assert(0.1 + 0.2 == 0.3_nearly);
}

ちなみに誤差範囲を変える場合はoperator()が使える

0.3_nearly(epsilon);

コンストラクタでも構築可能

NearlyDouble{v};
NearlyDouble{v, epsilon};

Viewing all articles
Browse latest Browse all 7910

Trending Articles