{ }codelper
do-while 循环浮点除法绝对值P0130

巴比伦平方根

在没有平方根按钮的计算器上,用巴比伦方法(牛顿法)求平方根。
第一行给出个数 t,接下来 t 行每行给出一个正整数 a。
对每个 a 按如下方式计算。
初始估计值 x 是 a。
计算新的估计值 y = (x + a ÷ x) ÷ 2,并把迭代次数加 1。
所有除法都是保留小数部分的浮点除法,浮点数是双精度(double)。
如果 y 与 x 之差的绝对值小于 0.000000001,或迭代次数达到 100,就停止。
否则把 x 换成 y,再计算一次。
所以至少会计算一次。
对每个 a 在一行中输出迭代次数和最后的 y,用一个空格隔开。
y 四舍五入到小数点后第六位输出。
为了不让四舍五入落在分界上,不会给出这样的 a:最后的 y 乘以 1000000 后的小数部分在 0.499 到 0.501 之间(含两端)。

输入
1 ≤ t ≤ 100,1 ≤ a ≤ 10^12,不会有使 y × 10^6 的小数部分在 0.499~0.501 之间的 a
输出
每个 a 一行:迭代次数和保留六位小数的 y
样例 1 · stdin
4
1
2
9
1000000000000
stdout
1 1.000000
5 1.414214
7 3.000000
26 1000000.000000
main.py
输入(stdin)

运行时这些行会依次作为 input()/标准输入传入(运行中不会等待输入)。

$ 就绪

请在电脑上打开以编写和运行代码。在手机上可以查看题目和题解。

其他语言怎么写?查看各语言题解 · 8 种语言题解

这是“先算一次再问要不要停”的循环,正适合 do-while。
C、C++、Java、C#、JS、TS 有 do { … } while (条件);,条件是“继续”的条件,所以写 >=。
Python 和 Craykon 没有 do-while,用 while True:/当 (真) 进入,在末尾用 break/跳出 离开。
用普通的 while 先判断的话,一开始 x 和 y 相等,一次也不会计算。
除法陷阱:Python / 以及 JS、TS、Craykon 的 / 总是浮点除法,但 Python // 是整数商。
在 Java、C#、C、C++ 中,a / x 至少一边是 double 才是浮点除法,所以要让 x 是 double(两边都是整数就只剩商)。
绝对值陷阱:估计值从上方逼近,所以 y - x 是负数。
不取绝对值而写 y - x < 1e-9,第一轮就会停。
浮点数绝对值:Python abs,JS/TS Math.abs,Java Math.abs,C# Math.Abs,C++ std::abs(<cmath>),Craykon 数学.绝对值。
C 的 abs 用于 int(<stdlib.h>),浮点差 0.5 会变成 0 而过早停止。
C 中要用 fabs(<math.h>)。
保留六位小数输出:Python f"{y:.6f}",JS/TS y.toFixed(6),C/C++ printf("%.6f"),Craykon 字符.保留小数(y, 6)。
Java String.format("%.6f") 和 C# ToString("F6") 会按计算机的区域设置可能输出逗号,所以要传入 Locale.ROOT、CultureInfo.InvariantCulture。
a 不超过 10^12 时,最后两个估计值之差是 double 的一格(约 10^-10),一定会停;但 a 大得多时这一格大于 10^-9,循环可能永远停不下来。
次数上限 100 就是这道保险。
排除四舍五入分界的原因:√999999999999 = 999999.99999949999…,对这个 double,C、C++、Python、JS、C# 按精确值舍入得到 999999.999999,而 Java String.format 和 Craykon 字符.保留小数 对最短表示 "999999.9999995" 舍入,输出 1000000.000000。

Pythonmain.py · 13 行
t = int(input())for _ in range(t):    a = int(input())    x = float(a)    count = 0    # 파이썬에는 do-while이 없다: while True로 들어가서 끝에서 멈출지 묻는다    while True:        y = (x + a / x) / 2  # /는 늘 실수 나눗셈        count += 1        if abs(y - x) < 1e-9 or count == 100:            break        x = y    print(count, f"{y:.6f}")
JavaScriptmain.js · 16 行
const [t, ...nums] = require("fs").readFileSync(0, "utf8").trim().split(/\s+/).map(Number);const out = [];for (let i = 0; i < t; i++) {  const a = nums[i];  let x = a;  let y = a;  let count = 0;  // 한 번은 꼭 계산한 뒤 멈출지 묻는다  do {    x = y;    y = (x + a / x) / 2;    count++;  } while (Math.abs(y - x) >= 1e-9 && count < 100);  out.push(count + " " + y.toFixed(6));}console.log(out.join("\n"));
TypeScriptmain.ts · 17 行
import { readFileSync } from "node:fs";const [t, ...nums]: number[] = readFileSync(0, "utf8").trim().split(/\s+/).map(Number);const out: string[] = [];for (let i = 0; i < t; i++) {  const a: number = nums[i];  let x: number = a;  let y: number = a;  let count = 0;  // 한 번은 꼭 계산한 뒤 멈출지 묻는다  do {    x = y;    y = (x + a / x) / 2;    count++;  } while (Math.abs(y - x) >= 1e-9 && count < 100);  out.push(count + " " + y.toFixed(6));}console.log(out.join("\n"));
JavaMain.java · 24 行
import java.util.Locale;import java.util.Scanner; public class Main {    public static void main(String[] args) {        Scanner sc = new Scanner(System.in);        int t = sc.nextInt();        StringBuilder out = new StringBuilder();        for (int i = 0; i < t; i++) {            long a = sc.nextLong();            double x;            double y = a;            int count = 0;            // 한 번은 꼭 계산한 뒤 멈출지 묻는다            do {                x = y;                y = (x + a / x) / 2; // x가 double이라 a / x도 실수 나눗셈                count++;            } while (Math.abs(y - x) >= 1e-9 && count < 100);            out.append(count).append(' ').append(String.format(Locale.ROOT, "%.6f", y)).append('\n');        }        System.out.print(out);    }}
C#Main.cs · 28 行
using System;using System.Globalization;using System.Text; class Program{    static void Main()    {        int t = int.Parse(Console.ReadLine());        var sb = new StringBuilder();        for (int i = 0; i < t; i++)        {            long a = long.Parse(Console.ReadLine());            double x;            double y = a;            int count = 0;            // 한 번은 꼭 계산한 뒤 멈출지 묻는다            do            {                x = y;                y = (x + a / x) / 2; // x가 double이라 a / x도 실수 나눗셈                count++;            } while (Math.Abs(y - x) >= 1e-9 && count < 100);            sb.Append(count).Append(' ').Append(y.ToString("F6", CultureInfo.InvariantCulture)).Append('\n');        }        Console.Write(sb);    }}
C++main.cpp · 22 行
#include <cmath>#include <cstdio>#include <iostream> int main() {    int t;    std::cin >> t;    for (int i = 0; i < t; i++) {        long long a;        std::cin >> a;        double x;        double y = static_cast<double>(a);        int count = 0;        // 한 번은 꼭 계산한 뒤 멈출지 묻는다        do {            x = y;            y = (x + a / x) / 2; // x가 double이라 a / x도 실수 나눗셈            count++;        } while (std::abs(y - x) >= 1e-9 && count < 100);        std::printf("%d %.6f\n", count, y);    }}
Cmain.c · 22 行
#include <math.h>#include <stdio.h> int main(void) {    int t;    scanf("%d", &t);    for (int i = 0; i < t; i++) {        long long a;        scanf("%lld", &a);        double x;        double y = (double)a;        int count = 0;        // 한 번은 꼭 계산한 뒤 멈출지 묻는다        do {            x = y;            y = (x + a / x) / 2; // x가 double이라 a / x도 실수 나눗셈            count++;        } while (fabs(y - x) >= 1e-9 && count < 100); // 실수 절댓값은 fabs        printf("%d %.6f\n", count, y);    }    return 0;}
Craykonmain.cray · 20 行
t = 控制台.读行().转数字();循环 (i = 0; i < t; i++){    a = 控制台.读行().转数字();    x = a;    y = a;    count = 0;    // 크레이콘에는 do-while이 없다: 동안 (참)으로 들어가서 끝에서 멈출지 묻는다    当 (真)    {        y = (x + a / x) / 2; // /는 늘 실수 나눗셈        count += 1;        如果 (数学.绝对值(y - x) < 1e-9 || count == 100)        {            跳出;        }        x = y;    }    控制台.输出(count + " " + 字符.保留小数(y, 6));}