将数字舍入到C中的2个小数位

201 c floating-point decimal

如何将浮点数(例如37.777779)舍入到C中的两位小数(37.78)?

Dal*_*und 389

如果您只想将数字四舍五入以进行输出,那么"%.2f"格式字符串确实是正确的答案.但是,如果您确实要将浮点值舍入以进行进一步计算,则类似以下内容的工作方式:

#include <math.h>

float val = 37.777779;

float rounded_down = floorf(val * 100) / 100;   /* Result: 37.77 */
float nearest = roundf(val * 100) / 100;  /* Result: 37.78 */
float rounded_up = ceilf(val * 100) / 100;      /* Result: 37.78 */
Run Code Online (Sandbox Code Playgroud)

请注意,您可能要选择三种不同的舍入规则:向下舍入(即,在两位小数后截断),四舍五入到最近,舍入.通常,你想要舍入到最近.

正如其他几个人所指出的那样,由于浮点表示的怪癖,这些舍入值可能不是"明显的"十进制值,但它们将非常接近.

关于四舍五入的更多信息(特别是关于舍入到最近的打破平局规则),请参阅关于舍入的维基百科文章.

  • 可以修改它以支持舍入到任意精度吗? (4认同)
  • 是的.要在k个小数位后舍入,请使用10 ^ k的比例因子.如果您手动写出一些十进制值并以10的倍数播放,这应该很容易看出.假设您正在使用值1.23456789,并希望将其舍入到3位小数.您可以使用的操作是*舍入到整数*.那么,如何移动前三个小数位,使它们只剩下小数点?我希望很明显你乘以10 ^ 3.现在,您可以将该值舍入为整数.接下来,将三个低位数字除以10 ^ 3. (3认同)
  • @DaleHagglung前者,谢谢.调整是用pow替换100(10,(int)desiredPrecision)? (2认同)

And*_*son 77

printf("%.2f", 37.777779);
Run Code Online (Sandbox Code Playgroud)

  • @albert 这还具有不会丢失 `float` 范围的优点,因为 `val * 100` 可能会溢出。 (2认同)
  • [此处](/sf/ask/1688462191/),讨论了“%.2f”是依赖于实现的。 (2认同)

Joh*_*ter 42

假设你正在谈论打印的价值,那么Andrew ColesonAraK的回答是正确的:

printf("%.2f", 37.777779);
Run Code Online (Sandbox Code Playgroud)

但请注意,如果你的目标是将数字四舍五入到37.78用于内部使用(例如与另一个值进行比较),那么这不是一个好主意,因为浮点数的工作方式:你通常不会想要对浮点进行相等比较,而是使用目标值+/- sigma值.或者将数字编码为具有已知精度的字符串,并进行比较.

请参阅Greg Hewgill对相关问题的回答中的链接,该问题还包括为什么不应使用浮点进行财务计算.


Dan*_*iil 22

这个怎么样:

float value = 37.777779;
float rounded = ((int)(value * 100 + .5) / 100.0);
Run Code Online (Sandbox Code Playgroud)

  • @therefromhere:(a)你是对的(b)这是什么?高中考试? (28认同)
  • -1:a)这对负数不起作用(好的,这个例子是正数但仍然).b)你没有提到在float中存储精确的十进制值是不可能的 (4认同)
  • @Daniil 评论中的_舍入规则_ 是 __round 到最接近的__ (2认同)

Ara*_*raK 20

printf("%.2f", 37.777779);
Run Code Online (Sandbox Code Playgroud)

如果要写入C-string:

char number[24]; // dummy size, you should take care of the size!
sprintf(number, "%.2f", 37.777779);
Run Code Online (Sandbox Code Playgroud)

  • C89只允许/**/ - 样式,C99引入了对//风格的支持.使用蹩脚/旧编译器(或强制C89模式),你将无法使用//风格.话虽如此,它是2009年,让我们考虑它们的C和C++风格. (5认同)

And*_*ton 11

没有办法将a 舍入float到另一个,float因为舍入float可能无法表示(浮点数的限制).例如,假设您将37.777779舍入到37.78,但最接近的可表示数字是37.781.

但是,您可以float使用格式字符串函数"舍入"a .

  • 这与说"没有办法划分两个浮点数并获得浮点数没有什么不同,因为划分的结果可能无法表示",这可能是正确的但是无关紧要.浮游物总是不精确的,即使对于基本的添加也是如此; 假设总是你实际得到的是"最接近完全舍入答案的浮动". (2认同)

syn*_*tik 7

此外,如果您使用的是C++,则只需创建如下函数:

string prd(const double x, const int decDigits) {
    stringstream ss;
    ss << fixed;
    ss.precision(decDigits); // set # places after decimal
    ss << x;
    return ss.str();
}
Run Code Online (Sandbox Code Playgroud)

然后,您可以使用以下代码输出任何myDoublen小数点后位置的双精度数:

std::cout << prd(myDouble,n);
Run Code Online (Sandbox Code Playgroud)


Zer*_*ool 6

你仍然可以使用:

float ceilf(float x); // don't forget #include <math.h> and link with -lm.
Run Code Online (Sandbox Code Playgroud)

例:

float valueToRound = 37.777779;
float roundedValue = ceilf(valueToRound * 100) / 100;
Run Code Online (Sandbox Code Playgroud)


syn*_*tik 6

在C++中(或在C中使用C风格的强制转换),您可以创建函数:

/* Function to control # of decimal places to be output for x */
double showDecimals(const double& x, const int& numDecimals) {
    int y=x;
    double z=x-y;
    double m=pow(10,numDecimals);
    double q=z*m;
    double r=round(q);

    return static_cast<double>(y)+(1.0/m)*r;
}
Run Code Online (Sandbox Code Playgroud)

然后std::cout << showDecimals(37.777779,2);会产生:37.78.

显然你并不需要在该函数中创建所有5个变量,但我将它们留在那里以便您可以看到逻辑.可能有更简单的解决方案,但这对我很有用 - 特别是因为它允许我根据需要调整小数位后的位数.


Mar*_*ery 5

printf为此,请始终使用功能族。即使您希望以浮点数形式获取值,也最好使用snprintf将取整后的值作为字符串获取,然后使用以下方法将其解析回atof

#include <math.h>
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>

double dround(double val, int dp) {
    int charsNeeded = 1 + snprintf(NULL, 0, "%.*f", dp, val);
    char *buffer = malloc(charsNeeded);
    snprintf(buffer, charsNeeded, "%.*f", dp, val);
    double result = atof(buffer);
    free(buffer);
    return result;
}
Run Code Online (Sandbox Code Playgroud)

我之所以这样说,是因为目前投票最多的答案以及此处的其他几个答案所示的方法-乘以100,四舍五入为最接近的整数,然后再除以100,则该方法存在两种缺陷:

  • 对于某些值,由于舍入点数不精确,乘以100会将确定舍入方向的十进制数字从4更改为5,反之亦然,因此它将向错误的方向取整
  • 对于某些值,相乘然后除以100并不是往返,这意味着即使不进行舍入,最终结果也将是错误的

为了说明第一种错误-舍入方向有时是错误的-尝试运行以下程序:

#include <math.h>
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>

double dround(double val, int dp) {
    int charsNeeded = 1 + snprintf(NULL, 0, "%.*f", dp, val);
    char *buffer = malloc(charsNeeded);
    snprintf(buffer, charsNeeded, "%.*f", dp, val);
    double result = atof(buffer);
    free(buffer);
    return result;
}
Run Code Online (Sandbox Code Playgroud)

您将看到以下输出:

int main(void) {
    // This number is EXACTLY representable as a double
    double x = 0.01499999999999999944488848768742172978818416595458984375;

    printf("x: %.50f\n", x);

    double res1 = dround(x, 2);
    double res2 = round(100 * x) / 100;

    printf("Rounded with snprintf: %.50f\n", res1);
    printf("Rounded with round, then divided: %.50f\n", res2);
}
Run Code Online (Sandbox Code Playgroud)

请注意,我们开始时的值小于0.015,因此将其舍入到小数点后两位时的数学正确答案为0.01。当然,不能精确地将 0.01 表示为双精度数,但是我们希望我们的结果是最接近0.01的双精度数。使用snprintf给出结果,但是使用round(100 * x) / 1000.02,这是错误的。为什么?因为100 * x给出的结果恰好是1.5。因此,乘以100会更改正确的方向以四舍五入。

为了说明第二类型的错误-结果有时是错误的,由于* 100/ 100没有真正被对方的逆-我们可以做一个类似的工作具有非常大的数字:

x: 0.01499999999999999944488848768742172978818416595459
Rounded with snprintf: 0.01000000000000000020816681711721685132943093776703
Rounded with round, then divided: 0.02000000000000000041633363423443370265886187553406
Run Code Online (Sandbox Code Playgroud)

现在,我们的数字甚至不占分数。它是一个整数值,仅与type存储在一起double。因此,四舍五入后的结果应该与开始时的数字相同,对吗?

如果您运行上面的程序,您将看到:

x: 8631192423766613.0
Rounded with snprintf: 8631192423766613.0
Rounded with round, then divided: 8631192423766612.0
Run Code Online (Sandbox Code Playgroud)

哎呀。我们的snprintf方法再次返回正确的结果,但是“乘-乘-乘-乘-乘-除”方法失败。这是因为数学上正确的值8631192423766613.0 * 100863119242376661300.0是不是作为一个双精确表示; 最接近的值是863119242376661248.0。当您将其除以100时,得到8631192423766612.0的数字与开始时的数字不同。

希望这是一个充分的演示,证明roundf舍入到小数点后一位的位数是无效的,您应该snprintf改为使用。如果这对您来说真是骇人听闻的破解,那么也许您会被CPython的基本功所知。

  • +1 是我的答案和类似答案出现问题的具体示例,这要归功于 IEEE 浮点的奇怪之处,并提供了一个简单的替代方案。很久以前,我就意识到,在印刷上付出了很多努力,并且朋友们对我来说,它们对于往返浮点值是安全的。我猜当时完成的工作可能会出现在这里。 (2认同)