さらに先へ · 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 정수는 넘치지 않아요const lines = require("fs").readFileSync(0, "utf8").trim().split("\n");const [a, b] = lines[0].split(" ").map(Number);const x = Number(lines[1]);const h = Number(lines[2]);const big = Number(lines[3]); console.log((a / b).toFixed(2)); // 수가 하나라 / 는 늘 소수까지const s = 0.1 + 0.2; // 0.30000000000000004console.log(Math.abs(s - 0.3) < 1e-9 ? "close" : "far");console.log(Math.trunc(x), Math.floor(x)); // -3 -4console.log(Math.round(h)); // 2.5 → 3 (단, -2.5 → -2)console.log(big + 1); // 2^53까지는 정확해요import { readFileSync } from "node:fs";const lines: string[] = readFileSync(0, "utf8").trim().split("\n");const [a, b]: number[] = lines[0].split(" ").map(Number);const x: number = Number(lines[1]);const h: number = Number(lines[2]);const big: number = Number(lines[3]); console.log((a / b).toFixed(2)); // 수가 하나라 / 는 늘 소수까지const s: number = 0.1 + 0.2; // 0.30000000000000004console.log(Math.abs(s - 0.3) < 1e-9 ? "close" : "far");console.log(Math.trunc(x), Math.floor(x)); // -3 -4console.log(Math.round(h)); // 2.5 → 3 (단, -2.5 → -2)console.log(big + 1); // 2^53까지는 정확해요import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner in = new Scanner(System.in); int a = in.nextInt(), b = in.nextInt(); double x = Double.parseDouble(in.next()); double h = Double.parseDouble(in.next()); int big = in.nextInt(); System.out.println(String.format("%.2f", (double) a / b)); // a / b만 쓰면 0 double s = 0.1 + 0.2; // 0.30000000000000004 System.out.println(Math.abs(s - 0.3) < 1e-9 ? "close" : "far"); System.out.println((int) x + " " + (int) Math.floor(x)); // -3 -4 System.out.println(Math.round(h)); // 2.5 → 3 (long) System.out.println((long) big + 1); // big + 1은 int라 -2147483648 }}using System; class Program{ static void Main() { string[] ab = Console.ReadLine().Split(' '); int a = int.Parse(ab[0]), b = int.Parse(ab[1]); double x = double.Parse(Console.ReadLine()); double h = double.Parse(Console.ReadLine()); int big = int.Parse(Console.ReadLine()); Console.WriteLine(((double)a / b).ToString("F2")); // a / b만 쓰면 0 double s = 0.1 + 0.2; // 0.30000000000000004 Console.WriteLine(Math.Abs(s - 0.3) < 1e-9 ? "close" : "far"); Console.WriteLine($"{(int)x} {Math.Floor(x)}"); // -3 -4 Console.WriteLine(Math.Round(h, MidpointRounding.AwayFromZero)); // 기본은 짝수 쪽(2) Console.WriteLine((long)big + 1); // big + 1은 int라 -2147483648 }}#include <cmath>#include <iomanip>#include <iostream> int main() { int a, b; double x, h; long long big; std::cin >> a >> b >> x >> h >> big; std::cout << std::fixed << std::setprecision(2) << (double)a / b << '\n'; double s = 0.1 + 0.2; // 0.30000000000000004 std::cout << (std::abs(s - 0.3) < 1e-9 ? "close" : "far") << '\n'; std::cout << (int)x << ' ' << (int)std::floor(x) << '\n'; // -3 -4 std::cout << std::llround(h) << '\n'; // 0에서 먼 쪽 → 3 std::cout << big + 1 << '\n'; // int였다면 넘침(정의되지 않은 동작)}#include <stdio.h>#include <math.h> int main(void) { int a, b; double x, h; long long big; scanf("%d %d %lf %lf %lld", &a, &b, &x, &h, &big); printf("%.2f\n", (double)a / b); /* a / b만 쓰면 0 */ double s = 0.1 + 0.2; /* 0.30000000000000004 */ printf("%s\n", fabs(s - 0.3) < 1e-9 ? "close" : "far"); printf("%d %d\n", (int)x, (int)floor(x)); /* -3 -4 */ printf("%.0f\n", round(h)); /* round는 0에서 먼 쪽 */ printf("%lld\n", big + 1); /* int였다면 넘침(정의되지 않은 동작) */ return 0;}ab = コンソール.数読み();a = ab[0];b = ab[1];x = コンソール.行読み().数に();h = コンソール.行読み().数に();big = コンソール.行読み().数に(); // / 는 늘 소수까지 나눠요コンソール.書く(文字.桁そろえ(a / b, 2));s = 0.1 + 0.2;もし (数学.絶対値(s - 0.3) < 1e-9){ コンソール.書く("close");}でなければ{ コンソール.書く("far");}コンソール.書く(数学.商(x, 1) + " " + 数学.切り下げ(x));コンソール.書く(数学.四捨五入(h));コンソール.書く(big + 1);2 3 -3.7 2.5 2147483647
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 ビットの整数に入れます。
言語ごとの書き方
| Python | f"{x:.2f}" — round(2.5) は 2(偶数側)。int(x) は切り捨て。整数に大きさの制限がないので、あふれません |
|---|---|
| JavaScript | x.toFixed(2) — Math.round は +∞ 側(2.5 → 3、-2.5 → -2)、切り捨ては Math.trunc(x)。数が 1 種類(浮動小数点数)なので整数が正確なのは 2^53(Number.MAX_SAFE_INTEGER)までで、それより大きければ BigInt(123n) |
| TypeScript | JavaScript と同じです |
| Java | String.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 |
| C | printf("%.2f", x) — 丸めとオーバーフローは C++ と同じです。long は Windows では 32 ビットなので long long が安全です |
| Craykon | 文字.桁そろえ(x, 2) — 数学.四捨五入 は 0 から遠い側(-2.5 → -3)。数学.商(x, 1) が切り捨て、数学.切り下げ(x) が切り下げ(名前が紛らわしいので注意)。数が 1 種類なので整数が正確なのは 2^53 までで、BigInt はありません |
よくある間違い
Craykon の出力が 0.3 に見えるCraykon
コンソール.書く(0.1 + 0.2);
コンソール.書く(0.1 + 0.2 == 0.3);→ 0.3 と false — 小数第 9 位で丸めて表示するだけで、値は 0.30000000000000004 です
表示を信じず、差がごく小さいかで比べます。
整数どうしで割ったら小数が消えた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)に入れます。
言語ごとの違い
浮動小数点数どうしを
差の絶対値がごく小さいか(
浮動小数点数どうしを
== で比べないでください。差の絶対値がごく小さいか(
abs(a - b) < 1e-9)を調べ、お金のように正確でなければならない値は 100 倍して整数(円・セント)で扱います。