はじめに
浮動小数点数型を使っていると、ニアイコールの判定をしたいことが多いですが、それを書くのが面倒くさかったので
ラッパークラスを作りました。
普通はこういう関数とか作っちゃえば十分なんだけどね
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};