C++四舍五入保留N位小数

11 篇文章 0 订阅

最近遇到这个问题,不难吧,不过搜网上的东西,没看到比较合心水的答案,自己搞了两种做法,请君侧耳为我听。

乘10法

这种想法最容易想到,就是将数字乘10,乘N次,加上0.5后取整,最后再除回去。加上0.5取整应该很容易理解,是等价于四舍五入的,因为X.49999……+0.5后取整是不会进位的,结果是X;而X.5000……1+0.5后取整会变成X+1(当然忽略double或float能表示的精度)。

比如:

23.3451保留两位小数,那么连续乘10,2次,得到2334.5,加上0.5后取整得到23345,再除回去,变成23.35,是不是对了呢?

简单代码如下:

#include <stdio.h>
#include <cmath>

typedef long long LL;

double round(double number, unsigned int bits) {
    LL integerPart = number;
    number -= integerPart;
    for (unsigned int i = 0; i < bits; ++i)
        number *= 10;
    number = (LL) (number + 0.5);
    for (unsigned int i = 0; i < bits; ++i)
        number /= 10;
    return integerPart + number;
}

int main() {
    printf("%.2f\n", round(23.451528739, 1));
    printf("%.3f\n", round(23.451528739, 2));
    printf("%.4f\n", round(23.451528739, 3));
    printf("%.5f\n", round(23.451528739, 4));
    printf("%.6f\n", round(23.451528739, 5));
    printf("%.7f\n", round(23.451528739, 6));
    printf("%.8f\n", round(23.451528739, 7));

    return 0;
}

注意,printf也可以做到按多少位小数四舍五入输出,所以在输出的时候,我总是把printf输出的精度比我自己的函数高1个位,可以看出那个位总是为0,表示是我写的函数起的作用(而不是printf的影响),上面的示例程序的输出结果为:

23.50
23.450
23.4520
23.45150
23.451530
23.4515290
23.45152870

问题来了

这个做法有什么问题呢?
注意到上面是先将double的整数部分保留起来了,然后后面只是对number原来的小数部分乘以10而已,为什么要这样做呢?
因为,注意到后面做四舍五入的关键一步,要取整,如果不先把已知的整数部分去掉,那么总体乘以10^N后很容易就会溢出整数所能表示的范围了!

也就是说,这种方法的局限在于,需要将double转成数值范围相对很小的long long来表示,所以如果需要精确到很大的小数位的话,就坑了,因为long long最多能表示10^18左右的数字,如果想四舍五入到小数点后20+位的话……其实这样变态的需求一般不会有,而且也不可能会有,因为double本身的精确位数只能到15-16(参考:float与double的范围和精度)。

再者,注意原始的整数部分也是用long long来保存的,这样对于double的范围几乎就限制死了……有没有更好的方法呢?

会这样问的,肯定就是有啦,我发现可以使用cmath库的floor向下取整函数来辅助取整,而不需要通过long long类型来取整,这样的话,上面所说的两个约束就不存在了!!!

double round(double number, unsigned int bits) {
    double integerPart = floor(number);
    number -= integerPart;
    for (unsigned int i = 0; i < bits; ++i)
        number *= 10;
    number = floor(number + 0.5);
    for (unsigned int i = 0; i < bits; ++i)
        number /= 10;
    return integerPart + number;
}

使用C++的输出流功能

C++的输出流可以通过setprecision(N)和fixed合作来进行精确到小数点后第N为四舍五入!
但是只能输出到控制台???我想做成像上面那样的一个函数,结果作为返回值返回。
这个时候简单借助stringstream就好了!!!示例如下:

#include <iostream>
#include <sstream>
#include <iomanip>
using namespace std;

double round(double number, unsigned int bits) {
    stringstream ss;
    ss << fixed << setprecision(bits) << number;
    ss >> number;
    return number;
}

int main() {
    double number = 3.1415926535897932;
    cout << fixed << showpoint << setprecision(15);
    cout << "一开始number = " << number << endl;

    for (int i = 0; i < 15; ++i) {
        cout << "number保留" << i << "位小数后为: " 
            << round(number, i) << endl;
    }

    return 0;
}

输出为:

一开始number = 3.141592653589793
number保留0位小数后为: 3.000000000000000
number保留1位小数后为: 3.100000000000000
number保留2位小数后为: 3.140000000000000
number保留3位小数后为: 3.142000000000000
number保留4位小数后为: 3.141600000000000
number保留5位小数后为: 3.141590000000000
number保留6位小数后为: 3.141593000000000
number保留7位小数后为: 3.141592700000000
number保留8位小数后为: 3.141592650000000
number保留9位小数后为: 3.141592654000000
number保留10位小数后为: 3.141592653600000
number保留11位小数后为: 3.141592653590000
number保留12位小数后为: 3.141592653590000
number保留13位小数后为: 3.141592653589800
number保留14位小数后为: 3.141592653589790

分析一下,这个输出流的功能是C++的标准库支持的,所以其可靠性是可以信任的,没有上面的做法那么多控制的考虑。

问题来了

它库是怎么实现的????

真心好奇,希望有知道的朋友发邮件告诉我一声,万谢!

不然我只能等有空去找源码看了^_^

  • 20
    点赞
  • 43
    收藏
    觉得还不错? 一键收藏
  • 11
    评论
1.阶乘尾部0的个数 问题描述 给定非负整数n,计算n的阶乘尾部0的个数。 输入 输入数据有若干行,每行上有一个非负整数n,对应一种情形。 输出 对于每一种情形,直接输出结果、换行。 2.判断算式的正确性 问题描述 给定一个算式,该算式中只含一个四则运算符号,操作数及结果均为整数。要求判断该算式的正确性(规定:除法必须除尽才算正确)。 输入 输入数据有若干行,每行上有一个算式,对应一种情形。 输出 对于每一种情形,直接输出T(表示正确)或F(表示错误),换行。 3.计算一系列实数的个数、最小值、最大值和“平均值” 问题描述 给定若干个(大于0且不超过1024)实数,计算其中数据的个数(n)、最小值、最大值,以及“平均值”。此处的“平均值”定义为,若n>2,则需去掉一个最大值、一个最小值,剩下的n-2个数据的算术平均值;若n为1或2,则为普通的算术平均值。 输入 输入数据有多行,每一行上有若干个实数,对应一种情形(数据之间用一个空格字符分隔)。 输出 对于每一种情形,依次输出数据的个数、最小值、最大值、平均值、换行。其中数据之间用逗号和空格分隔,浮点型数据保留2小数。 4.字符串倒置 问题描述 对于给定的字符串,将其按字符倒置。 输入 输入数据有多行。每一行为一个字符串(字符串长度小于1024,其中可能含有空格字符),对应一种情形。 输出 对于每一种情形,输出结果并换行。 5.字符在字符串中首次出现的置 问题描述 给定一个字符、一个字符串(字符串的长度小于1024),计算该字符在字符串中首次出现的置。 输入 输入数据有若干行。每两行对应一种情形,这两行中的第一行上有一个字符(请注意将该字符后面的换行字符“吃掉”),第二行上有一个字符串(字符串中可能含有空白字符)。 输出 对于每一种情形,输出计算结果(若字符不在字符串中,则输出0),然后换行。
6折纸的厚度 问题描述 假定有一张充分大的纸,并且可以进行许多次对折。若单张纸的厚度为x,计算经过多少次对折后,折纸的厚度超过珠穆朗玛峰的高度8844.43米(8844430毫米)。 输入 输入数据仅有一行。该行有若干个数据分别表示单纸张的厚度(以毫米为单),对应每一种情形。 输出 对于每一种情形,直接输出结果,换行。 7 计算和式 问题描述 给定两个正整数a(1≤a≤9)和n(n≥1),计算[a]+[aa]+…+[a…a]的值。例如:a为2,n为5时,则计算2+22+222+2222+22222。 输入 输入数据有若干行。每行有两个整数,分别是a和n,对应每一种情形。 输出 对于每一种情形,直接输出计算结果、换行。 8 乘方计算 问题描述 给定一个unsigned long long型数据$x$,计算$x^2, x^3,\cdots$直到$x^5$或溢出为止。 输入 输入数据有若干行。每行上有一个unsigned long long型整数,对应一种情形。 输出 对于每一种情形,依次输出$x, x^2, x^3, x^4, x^5$的值,不输出计算溢出的数值。数据项之间用逗号、空格分隔。 9 将非负十进制整数各倒置 问题描述 给定一个非负整数,将其按十进制各倒置。 输入 输入数据有若干行。每行上有一个非负整数,对应一种情形。 输出 对于每一种情形,直接输出结果,换行。 10 判断同构数 问题描述 给定一个十进制正整数,判断其是否为同构数。所谓同构数是这样的一些数,它出现在其平方数的右边,例如5是出现在$5^2=25$右边的数,25是出现在$25^2=625$右边的数,所以5和25都是同构数。 输入 输入数据有若干行。每行上有一个正整数,对应一种情形。 输出 对于每一种情形,输出Y(表示是同构数)或N(表示不是同构数),换行。 11 兑换钱币 问题描述 对于给定的人民币金额n(分),问有多少种方案将其兑换成1分、2分、5分。 输入 输入数据有若干行。每行上有一个正整数表示以分为单的人民币金额n,对应一种情形。 输出 对于每一种情形,输出结果、换行。 12 给定5个实数的算术平均值和几何平均值 问题描述 给定5个实数,计算其算术平均值及几何平均值。 输入 输入数据仅有若干行,每行上有5个双精度浮点型数据,对应一种情形。 输出 对于每一种情形,输出算术平均值、逗号、空格、几何平均值、换行(浮点数保留2小数)。 13 指示灯控制 问题描述 有m(m<100)盏灯排成一排(从1到m按顺序依次编号)。灯的开关均为点触式的(即点一次开、再点一次则关)。现有n个人(从1到n依次编号)。第一个人(1号)将灯全部关闭。第二个人(2号)将所有2的倍数编号的灯的开关点一下。第三个人(3号)将所有3的倍数编号的灯的开关点一下。依此类推,当n个人完成其操作后,计算灯亮的数目。 输入 输入数据有若干行。每行上有两个非负整数对应一种情形的m和n。 输出 对于每一种情形,输出计算结果、换行。 14 分类统计字符串中各类字符的个数 问题描述 给定一个字符串,统计其中空格、数码、大写字母、小写字母的个数。 输入 输入数据有若干行。每一行上有一个字符串(长度小于1024字符),对应一种情形。 输出 对于每一种情形,依次输出空格的个数、数码字符的个数、大写字母的个数、小写字母的个数,数据之间用逗号及空格字符分隔,然后换行。 15 态度决定一切 问题描述 将英文26个字母A~Z,或a~z对应到整数1~26,则态度 Attitude 对应的数字之和为100。编程计算给定的字符串对应的数字之和(规定非英文字母的字符均对应0)。 输入 输入数据有多行,每行上有一个字符串(长度小于1024)对应一种情形。 输出 对于每一种情况,输出结果、换行。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值