学习 › JavaScript
进阶 · 20
整数与浮点数
为什么 0.1 + 0.2 不等于 0.3,固定小数位输出,舍入规则,以及整数溢出。
数有两种:整数和浮点数。
整数精确但有上限;浮点数能保存小数,但会有极小的误差。
下面的例子把除法结果输出到小数点后两位,再依次演示截断、向下取整、四舍五入和大数加一。
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)用二进制存储,所以会产生极小的误差。 0.1 + 0.2在所有语言里都是0.30000000000000004。
所有语言的double都是同一种 IEEE 754 双精度,所以误差也完全一样。- 正好一半(2.5)往哪边舍入,各语言不同。
有三种规则:向偶数(银行家舍入)、向 +∞、远离 0。 - 把浮点数转换成整数时,小数部分向 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)。只有一种数(双精度浮点数),整数只在 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) 是向下取整。只有一种数,整数只在 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.3Craykon
控制台.输出(0.1 + 0.2);
控制台.输出(0.1 + 0.2 == 0.3);→ 0.3 和 false — 只是在小数点后第九位四舍五入后显示,值本身是 0.30000000000000004
不要相信显示的值,要看差是否足够小。
各语言的差异
不要用
应该看差的绝对值是否足够小(
不要用
== 比较两个浮点数。应该看差的绝对值是否足够小(
abs(a - b) < 1e-9);像金额这种必须精确的值,就乘以 100,用整数(分)来处理。