英語613639 views
高校物理160203 views
りんご209490 views
MathPython497343 views
いろは3010814 views
高校化学2924392 views
Computer368106 views
数学講師2886227 views
LaTeX962153 views
中学数学623755 views
Help
Tools
NewsSpreadsheetCalendarBookkeepingSlidesTier ListPen ToolIllustrationCrayonWatercolorPixel ArtASCII ArtPerspectiveEndless StairsGraphMind MapER DiagramFamily TreeMemeCurved TextImage EditorMosaicRetro FilterPencil SketchSwirl EffectOCR/HighlighterMakeup EditorFaviconVideo TrimmerScrolling VideoVideo TitleColor PickerColor ExtractorBonfireFireworksWater RippleWater SplashBreaking GlassWood GrainMarble TextureCSS ButtonIcon MakerBar ChartGrouped Bar ChartStacked Bar ChartPie ChartLine ChartArea ChartStacked Area ChartScatter Plot3D Bar Chart3D Pie ChartBar Chart RaceBubble ChartPopulation PyramidPictogramEarningsCandlestick ChartInvestment RiskMortgage SimulatorCalculatorMatrix CalculatorFunction GraphPolynomial ExpansionVenn DiagramField VisualizerRubik's Cube Group TheoryTraveling SalesmanVoronoi and DelaunayFractalColumn ArithmeticDraw Math FiguresArithmetic AnimationArithmetic Word ProblemsCounting with Tree DiagramsCube NetsRolling DiceCross SectionsMotion PathMechanicsWavesElectromagnetic WavesLight and LensesThermodynamicsHow Semiconductors WorkMolecular StructuresAtomic OrbitalsElectrochemical CellsChemical EquilibriumCrystal LatticesBuffer pHPeriodic TableComplex IonsDNA Double HelixCell DivisionMembrane ChannelsNerve ImpulseMuscle ContractionHormones and HomeostasisRock ClassificationWeatherConstellationsSolar and Lunar Eclipses3D ModelingFloor PlanSeismic StructuresIntersection TurnMaglevCooking AnimationOrigamiLive Viewer CountGeoJSON MapRailway MapPopulation MapCrime MapLand Price MapSchool MapShrine and Castle MapHouse of Representatives MapWord MapSolitaireReversiHakoiri MusumeChessHamburgerRippleSlide Puzzle MakerNeon PinballNovel MakerJapanese Typing PracticePiano Score EditorMusic TheoryShogi StrategyPiano Rhythm Game

English

浮動小数点の誤差と対処法(isclose, Decimal)|Python

Python の float は IEEE 754 倍精度浮動小数点数であり、10 進小数を 2 進数で近似している。そのため、人間には単純に見える計算でも誤差が生じることがある。この誤差を理解し、適切に対処することはプログラマにとって重要なスキルだ。

誤差が生じる例

0.1 は 10 進数では有限小数だが、2 進数では無限小数になる。そのため、メモリ上では近似値として保存される。

print(0.1 + 0.1 + 0.1)  # 0.30000000000000004
print(0.1 + 0.2)  # 0.30000000000000004
print(0.1 + 0.2 == 0.3)  # False

この結果は直感に反するが、浮動小数点数の仕組みを知れば納得できる。0.1 を 2 進数に変換すると 0.0001100110011... と循環小数になり、有限ビットでは打ち切らざるを得ない。

内部表現の確認

float.hex() で 16 進浮動小数点表記を確認できる。

print((0.1).hex())  # 0x1.999999999999ap-4
print((0.2).hex())  # 0x1.999999999999ap-3
print((0.3).hex())  # 0x1.3333333333333p-2

末尾の 999... が循環を示唆している。0.1 + 0.2 の結果と 0.3 は内部表現がわずかに異なる。

等価比較の問題

浮動小数点数同士を == で比較すると、誤差により意図しない結果になることがある。

# 計算結果が期待値と一致しない
result = 0.1 * 3
expected = 0.3
print(result == expected)  # False

# ループでも問題が起きる
x = 0.0
while x != 1.0:
    x += 0.1
    if x > 1.5:  # 安全のため脱出条件を追加
        print('無限ループを回避')
        break

x が正確に 1.0 にならないため、x != 1.0 が永遠に真になる可能性がある。

math.isclose() で許容誤差を指定

math.isclose() は 2 つの数値が「十分に近い」かどうかを判定する。相対許容誤差(rel_tol)と絶対許容誤差(abs_tol)を指定できる。

import math

a = 0.1 + 0.2
b = 0.3

print(a == b)  # False
print(math.isclose(a, b))  # True

デフォルトでは rel_tol=1e-9(相対誤差 10 億分の 1)、abs_tol=0.0 が設定されている。

import math

# 相対許容誤差を指定
print(math.isclose(1000.0, 1000.001, rel_tol=1e-5))  # True
print(math.isclose(1000.0, 1000.001, rel_tol=1e-7))  # False

# 絶対許容誤差を指定(0 に近い値の比較に有用)
print(math.isclose(0.0, 1e-10, abs_tol=1e-9))  # True
print(math.isclose(0.0, 1e-10, abs_tol=1e-11))  # False

abs_tol は 0 に近い値を比較するときに重要だ。相対誤差だけでは、0 との比較で常に False になってしまう。

対処法 1:整数で計算して最後に変換

金額計算など、小数点以下の桁数が決まっている場合は、整数(円やセントの最小単位)で計算し、表示時にだけ小数に変換する方法がある。

# 悪い例:浮動小数点で計算
price = 19.99
quantity = 3
total = price * quantity
print(total)  # 59.970000000000006

# 良い例:整数(セント単位)で計算
price_cents = 1999
quantity = 3
total_cents = price_cents * quantity
total_dollars = total_cents / 100
print(f'${total_dollars:.2f}')  # $59.97

対処法 2:Decimal を使う

decimal.Decimal は 10 進数を正確に表現できる。詳細は別記事「高精度な小数計算(decimal.Decimal)」を参照。

from decimal import Decimal

a = Decimal('0.1') + Decimal('0.2')
b = Decimal('0.3')
print(a == b)  # True

金融計算や会計処理では Decimal を使うべきだ。

対処法 3:Fraction を使う

有理数の範囲であれば fractions.Fraction で誤差なく計算できる。詳細は別記事「分数で正確に計算する(fractions.Fraction)」を参照。

from fractions import Fraction

a = Fraction(1, 10) + Fraction(2, 10)
b = Fraction(3, 10)
print(a == b)  # True

対処法の使い分け

math.isclose()

浮動小数点数のまま「だいたい等しい」を判定したいとき。科学計算や物理シミュレーション向き。

decimal.Decimal

10 進小数を正確に扱いたいとき。金額計算、会計処理、為替計算など。

fractions.Fraction

分数のまま正確に計算したいとき。数学的な厳密性が必要な場面。

整数演算

小数点以下の桁数が固定されているとき。最小単位に換算して整数で計算する。

誤差が問題にならないケース

すべての場面で浮動小数点数を避ける必要はない。以下のような場合は float で十分だ。

物理シミュレーション(誤差が結果に大きく影響しない)
機械学習(モデルの精度に比べて誤差は無視できる)
グラフィックス(画素レベルの誤差は人間に知覚できない)
統計計算(十分な桁数があれば問題ない)

重要なのは、浮動小数点数の誤差を理解した上で、用途に応じた適切な型を選ぶことだ。