{ }codelper

学ぶ › Python

さらに先へ · 20

整数と浮動小数点数

0.1 + 0.2 が 0.3 にならない理由、小数の桁そろえ、丸めのルール、整数のオーバーフロー。

数には整数と浮動小数点数の 2 種類があります。
整数は正確ですが限界があり、浮動小数点数は小数を持てますがごく小さな誤差が出ます。
下の例は割り算を小数第 2 位まで出力し、切り捨て・切り下げ・丸めと大きな数の足し算を順に見せます。

import math a, b = map(int, input().split())x = float(input())h = float(input())big = int(input()) print(f"{a / b:.2f}")                     # 실수 나눗셈, 소수 둘째 자리s = 0.1 + 0.2                             # 0.30000000000000004print("close" if abs(s - 0.3) < 1e-9 else "far")print(int(x), math.floor(x))              # 버림 -3, 내림 -4print(math.floor(h + 0.5))                # round(2.5)는 2(짝수 쪽)라서 직접print(big + 1)                            # Python 정수는 넘치지 않아요
stdin
2 3
-3.7
2.5
2147483647
stdout
0.67
close
-3 -4
3
2147483648

ポイント

  • 整数(int、long)は割り切れる数を正確に持ちます。
    浮動小数点数(double)は 2 進数で記録するので、ごく小さな誤差が出ます。
  • 0.1 + 0.2 はどの言語でも 0.30000000000000004 です。
    どの言語の double も同じ IEEE 754 倍精度なので、誤差もまったく同じです。
  • ちょうど半分(2.5)を丸める方向は言語ごとに違います。
    偶数側(銀行丸め)、+∞ 側、0 から遠い側の 3 通りがあります。
  • 浮動小数点数を整数に変換すると、小数部分は 0 の方向へ切り捨てられます。
    なので -3.7 は -3 になり、小さい側へ下げる floor(-3.7) は -4 です。
  • 整数にも限界があります。
    32 ビットの int は 2147483647(約 21 億)までなので、それより大きな数は 64 ビットの整数に入れます。

言語ごとの書き方

Pythonf"{x:.2f}" — round(2.5) は 2(偶数側)。
int(x) は切り捨て。
整数に大きさの制限がないので、あふれません
JavaScriptx.toFixed(2) — Math.round は +∞ 側(2.5 → 3、-2.5 → -2)、切り捨ては Math.trunc(x)。
数が 1 種類(浮動小数点数)なので整数が正確なのは 2^53(Number.MAX_SAFE_INTEGER)までで、それより大きければ BigInt(123n)
TypeScriptJavaScript と同じです
JavaString.format("%.2f", x) — Math.round は +∞ 側(-2.5 → -2)。
int があふれるとエラーなしで -2147483648 に回り込みます。
大きな数は long
C#x.ToString("F2") — Math.Round(2.5) は 2(偶数側)、Math.Round(x, MidpointRounding.AwayFromZero) で変えられます。
int があふれると回り込み、checked の中では OverflowException
C++std::fixed << std::setprecision(2) — 一度書くと、その後の浮動小数点数の出力にもずっと効きます。
round は 0 から遠い側(-2.5 → -3)。
符号付き整数のオーバーフローは未定義動作なので、大きな数は long long
Cprintf("%.2f", x) — 丸めとオーバーフローは C++ と同じです。
long は Windows では 32 ビットなので long long が安全です
Craykon文字.桁そろえ(x, 2) — 数学.四捨五入 は 0 から遠い側(-2.5 → -3)。
数学.商(x, 1) が切り捨て、数学.切り下げ(x) が切り下げ(名前が紛らわしいので注意)。
数が 1 種類なので整数が正確なのは 2^53 までで、BigInt はありません

よくある間違い

整数どうしで割ったら小数が消えたJava · C# · C · C++

int a = 7, b = 2;
double r = a / b;

→ r は 3.0 です。先に整数の割り算をしてから浮動小数点数にします

片方を先に浮動小数点数にします:(double)a / b。

int があふれたJava · C# · C · C++

int n = 2147483647;
n = n + 1;

→ Java・C# はエラーなしで -2147483648。C・C++ では未定義動作です

大きな数は long(C・C++ は long long)に入れます。

Craykon の出力が 0.3 に見えるCraykon

コンソール.書く(0.1 + 0.2);
コンソール.書く(0.1 + 0.2 == 0.3);

→ 0.3 と false — 小数第 9 位で丸めて表示するだけで、値は 0.30000000000000004 です

表示を信じず、差がごく小さいかで比べます。

言語ごとの違い
浮動小数点数どうしを == で比べないでください。
差の絶対値がごく小さいか(abs(a - b) < 1e-9)を調べ、お金のように正確でなければならない値は 100 倍して整数(円・セント)で扱います。

練習問題