如何从C语言中的浮点数提取小数部分?

67

我们如何提取浮点数的小数部分,并将小数部分和整数部分存储到两个不同的整数变量中?


1
我不明白如果你不知道要保留多少位小数,怎么可能将小数部分转换为整数。你能举几个例子吗? - Nosredna
1
如果您输入16.25,我想要得到25的整数值;同样地,如果数字是0.3215769,我想要得到3215769的整数。就像这样。 - Binu
除了使用字符串操作之外,还有其他方法可以完成这个任务吗? - Binu
floor()或casting可以轻松地获取整数部分,但是您实际上希望在“小数”部分存储什么?即使不考虑浮点(不)精度等问题,当您的候选浮点数是重复分数或无理数时,您希望发生什么? - HUAGHAGUAH
9
你如何区分1.5和1.005中的小数部分? - SingleNegationElimination
显示剩余6条评论
16个回答

105

你使用了modf函数:

double integral;
double fractional = modf(some_double, &integral);

你也可以将其转换为整数,但请注意可能会造成整数溢出。这时结果是不可预测的。


3
@penu 这是因为您在打印“double”时使用了“%d”。这是可行的代码:http://codepad.org/kPGKtcZi - Sam Protsenko
2
这段代码将 123.456745693850 中的 0.45674569385 截断为 0.456746。 - user5125586
@Richard,你可以在Stack Overflow上开一个新的问题,询问为什么它会在你的机器上这样做。我不知道。 - Johannes Schaub - litb
在我的机器上,modf的man页面给出了签名double modf(double x, double *iptr);,但我有一些编译器实现头文件使用float而不是double。也许这就是为什么截断到float可能会发生的原因...我想我会更喜欢使用int强制转换和减法解决方案来避免这个问题... - hbobenicio

70

试试这个:

int main() {
  double num = 23.345;
  int intpart = (int)num;
  double decpart = num - intpart;
  printf("Num = %f, intpart = %d, decpart = %f\n", num, intpart, decpart);
}

对我来说,它产生了:

Num = 23.345000, intpart = 23, decpart = 0.345000

这似乎就是您所询问的内容。


1
第一部分是正确的,但希望将结果作为小数部分,整数部分为345000。 - Binu
1
但我想将小数部分作为整数值获取,这意味着decpart = 345000。 - Binu
3
整数部分为 intpart,小数部分为 decpart,计算公式为 decpart = 100000*(num - intpart)。 - SingleNegationElimination
2
这种方法容易受到一些令人讨厌的浮点数问题的影响。尝试设置 num = 1.1 进行快速示例。作为浮点数,decpart 无法表示 0.1 - 0b101010
关于该方法的第二行:在'09年,C语言是否需要强制转换?还是只是为了澄清而存在? - General Grievance

17

最快、最明显的答案是:

#define N_DECIMAL_POINTS_PRECISION (1000) // n = 3. Three decimal points.

float f = 123.456;
int integerPart = (int)f;
int decimalPart = ((int)(f*N_DECIMAL_POINTS_PRECISION)%N_DECIMAL_POINTS_PRECISION);

您可以通过更改N_DECIMAL_POINTS_PRECISION来修改您需要的小数点位数。


1
只是为了好玩。让我们进行一次思维实验:更加混淆和复杂(即“难以维护的代码”)的解决方案可以走得更远,例如[位切片][1]浮点数或双精度数以获取实际的["整数位"和"小数位"][2]。 - Trevor Boyd Smith
我不确定你为什么要这样做……也许这种方法的优点在于能够直接捕获整数和小数部分,而不会因浮点舍入导致失去精度。 - Trevor Boyd Smith
以下是一些不完整的伪代码,以便让您了解:#define BIT_MASK1 /* 不确定 */ #define SHIFT /* 不确定 */ #define BIT_MASK2 /* 不确定 */ float f = 123.456; uint32_t * tmp = (uint32_t *)&f; int integerPart = (int)f; - Trevor Boyd Smith
int decimalPart = (((*tmp)&BIT_MASK1)>>SHIFT)&BIT_MASK2; - Trevor Boyd Smith

3
我创建了一个使用双精度浮点数的子程序,它返回2个整数值。

void double2Ints(double f, int p, int *i, int *d)
{ 
  // f = 浮点数, p=小数位数, i=整数部分, d=小数部分
  int   li; 
  int   prec=1;

  for(int x=p;x>0;x--) 
  {
    prec*=10;
  };  // 相当于power(10,p)

  li = (int) f;              // 获取整数部分
  *d = (int) ((f-li)*prec);  // 获取小数部分
  *i = li;
}

void test()
{ 
  double df = 3.14159265;
  int   i,d;
  for(int p=2;p<9;p++)
  {
    double2Ints(df, p, &i,&d); printf("d2i (%d) %f = %d.%d\r\n",p, df,i,d);
  }
}

2
使用浮点数减去取整后的值来得到其小数部分:
double fractional = some_double - floor(some_double);

这可以避免将浮点数强制转换为整数,如果浮点数非常大,整数值甚至无法容纳它,可能会导致溢出

此外,对于负值,此代码给出浮点数的正分数部分,因为floor()计算不大于输入值的最大整数值


尝试使用数字,例如10.2346789358,你将得到错误的返回值0.2346789357999999。 - user5125586
2
在“double”的上下文中,0.2346789357999999是几个正确答案之一,具体取决于值的编码方式、编译器标志、架构、处理器标志等。浮点类型不是十进制的。 - burito

1
这里有另一种方法:

#include <stdlib.h>
int main()
{
    char* inStr = "123.4567";         //the number we want to convert
    char* endptr;                     //unused char ptr for strtod

    char* loc = strchr(inStr, '.');
    long mantissa = strtod(loc+1, endptr);
    long whole = strtod(inStr, endptr);

    printf("whole: %d \n", whole);     //whole number portion
    printf("mantissa: %d", mantissa);  //decimal portion

}

http://codepad.org/jyHoBALU

输出:

whole: 123 
mantissa: 4567

1
你的代码在16位平台上无法运行,因为longs是32位,long double是53位。 - user2284570

1

我认为在这种情况下使用字符串是正确的方法,因为你不知道小数部分的位数。但是,它并不适用于所有情况(例如1.005),正如@SingleNegationElimination之前提到的那样。这是我的建议:

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

int main(void)
{
    char s_value[60], s_integral[60], s_fractional[60];
    int i, found = 0, count = 1, integral, fractional;

    scanf("%s", s_value);

    for (i = 0; s_value[i] != '\0'; i++)
    {
        if (!found)
        {
            if (s_value[i] == '.')
            {
                found = 1;
                s_integral[i] = '\0';
                continue;
            }
            s_integral[i] = s_value[i];
            count++;
        }
        else
            s_fractional[i - count] = s_value[i];
    }
    s_fractional[i - count] = '\0';

    integral = atoi(s_integral);
    fractional = atoi(s_fractional);
    printf("value = %s, integral = %d, fractional = %d\n",
        s_value, integral, fractional);

    return 0;
}

3
你解决问题的方法是任何人都不应该尝试使用的。 - risingballs

1
float num;
int intgPart;
float fracPart;
printf("Enter the positive floating point number: ");
scanf("%f", &num);

intgPart = (int)num;
fracPart = num - intgPart;

小数部分可以通过从原始的双精度值中减去整数部分来获得。

0

我的printf()不支持浮点数格式化。这是我将其作为两个整数打印的解决方案。将100更改为您喜欢的数字以增加精度。

我找到了这个

#define DEC(f) (uint32_t)(100*((f)-(float)((uint32_t)(f))))
#define INT(f) (uint32_t)(f)

DEC(3.1416..) = 14 (int) 
INT(3.1416..) = 3 (int)

请告诉我您对这个解决方案的看法。


0
 #include <stdio.h>
Int main ()
{
float f=56.75;
int a=(int)f;
int result=(f-a)*100;
printf ("integer = %d\n decimal part to integer 
 =%d\n",result);
}
Output:- 
integer =56
decimal part to integer = 75

你在这里假设小数部分只有两位数字。但实际情况可能并非如此。 - PJProudhon
1
虽然您的代码片段可能解决了问题,但您应该描述代码的目的(它如何解决问题)。此外,您可能需要查看https://stackoverflow.com/help/how-to-answer。 - Ahmad F

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