在C语言中将小数点向右移动

4

我对C语言比较陌生,当我执行下面的代码时,输出的值是12098而不是12099。

我知道处理小数总会涉及到一定程度的不准确性,但是否有一种方法可以每次精确地将小数点向右移动两位?

#include <stdio.h>

int main(void)
{
    int i;
    float f = 120.99;

    i = f * 100;
    printf("%d", i);
}

尝试这个 float i, f = 120.99f; - haccks
在标准C++中没有decimal类型,您需要像http://sourceforge.net/projects/stddecimal/这样的十进制类型。 - sp2danny
使用 ftoa() 并将每个数字复制到一个 char 缓冲区中,通过 isdigit() 跳过小数点字符。 - Alex Reynolds
i = f * 100 + 0.5f 可以避免在数字如 120.99 中出现问题。它会四舍五入到最近的整数(但对于负值,请使用 -0.5f)。 - JS1
如果您需要精确度,请使用整数类型。例如,在处理货币(例如银行账户)时使用浮点类型将是一场灾难。 - Josh
5个回答

5

使用round函数

float f = 120.99;
int i = round( f * 100.0 );

请注意,一个浮点数通常只有6或7位有效数字,因此存在一个最大值,超过这个最大值将无法正常转换。无法正确转换的最小浮点数值是131072.01。如果乘以100并四舍五入,则结果为13107202。
您可以通过使用双精度值来扩展数字范围,但即使双精度值也有限制范围(双精度值有16或17位有效数字)。例如,以下代码将打印出10000000000000098。
double d = 100000000000000.99;
uint64_t j = round( d * 100.0 );
printf( "%llu\n", j );

这只是一个例子,找到超过 double 精度的最小数字留给读者作为练习。


4

在整数上使用定点算术:

#include <stdio.h>

#define abs(x) ((x)<0 ? -(x) : (x))

int main(void)
{
    int d = 12099;
    int i = d * 100;
    printf("%d.%02d\n", d/100, abs(d)%100);
    printf("%d.%02d\n", i/100, abs(i)%100);
}

2
您的问题是浮点数在内部使用 IEEE-754 表示。这是基于二进制而不是十进制的。0.25 将有一个精确的表示,但 0.1 和 120.99 都没有。
实际发生的情况是,由于浮点数的不准确性,最接近小数值 120.99 乘以 100 的 ieee-754 浮点数略低于 12099,因此被截断为 12098。您的编译器应该警告您进行了从浮点数到整数的截断(我的编译器确实有)。
获取您期望的唯一可靠方法是在截断为整数之前将浮点数加上 0.5:
i = (f * 100) + 0.5

但是要注意,浮点数在处理十进制值时本质上是不准确的。
编辑:
当然,对于负数,应该是i = (f * 100) - 0.5...

小问题:IEEE-754确实指定了二进制和十进制浮点格式,而你的答案依赖于更为普遍的二进制格式。十进制格式在过去曾经有一定流行度,我猜想它们将因为这些问题而有所复兴。 - chux - Reinstate Monica
@chux:我引用了维基百科页面,因为它提供了更多的详细信息。但如果 OP 系统使用十进制 IEEE 754 标准,就不会出现任何问题 :-) - Serge Ballesta

1
如果您希望继续将数字作为浮点数进行操作,那么答案基本上是“不行”。对于小数字,有各种方法可以处理,但是随着数字变得越来越大,您会遇到问题。
如果您只想打印数字,则我的建议是将数字转换为字符串,然后将小数点移动到那里。这可能会稍微复杂,具体取决于您如何在字符串中表示数字(指数等)。
如果您希望此功能正常工作,并且不介意不使用浮点数,则建议研究任何一种固定小数库。

0

您可以使用

float f = 120.99f

或者

double f = 120.99

默认情况下,C将浮点值存储为双精度,因此如果您将它们存储在浮点变量中,则会发生隐式转换,这是不好的...
我认为这可以解决。


当您将double值存储在float变量中时,不会发生强制转换,而是发生隐式转换。 - The Paramagnetic Croissant
float是32位值,而double是64位值,它们的存储算法不同,因此在它们之间进行强制转换是危险的。您可以查看Wikipeadia获取详细信息。 - Parham Alvani
请不要教我浮点数。我非常清楚在不同精度的浮点数之间转换的缺陷。我指的是您回答中错误的措辞(按定义,“强制类型转换”是显式转换;因此,隐式转换不能称为“强制类型转换”)。 - The Paramagnetic Croissant

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接