【问题标题】:How to convert floating value to integer with exact precision like 123.3443 to 1233443?如何将浮点值转换为具有精确精度的整数,例如 123.3443 到 1233443?
【发布时间】:2012-03-27 12:14:36
【问题描述】:

示例代码:

int main() {
  float f = 123.542;
  int i = (int)f;
  printf("%d\n",i);
}

【问题讨论】:

  • 如果号码是1.999999999999怎么办?提示:您尝试做的事情是不可能的。 (或者至少用二进制浮点定义不好)
  • 也许您想要的是在接近零的某个部分的左侧获得尽可能多的浮点数(具有一定的容差)?
  • @Mysticial:浮点数实际上总是可能的,因为 FP 指数是 2 的幂。结果通常不是你想要的(例如,它只适用于精确二进制分数)。
  • 假设 32 位 IEEE 浮点数,您示例中 f 的精确十进制值将是 123.54199981689453125。这不是 printf 将打印的内容,因为它使用启发式方法打印最短的表示,如果将其解析回浮点数,则会为您提供完全相同的浮点数。我提到这一点是为了指出其他人是正确的,您的问题可能不够完善,无法提供好的答案。

标签: c floating-point type-conversion floating-point-precision


【解决方案1】:

123.3443 不能用浮点数精确表示——在 32 位浮点数中,它实际上表示为 16166984 / 131072,实际上是 123.34429931640625,而不是 123.3443。 (大约 6.8 x 10^-7。)

如果这确实是您想要的结果(可能不是),请查看 IEEE-754 浮点数是如何工作的,然后拿出您最喜欢的任意精度数学套件。一旦您了解“幕后”发生的事情,生成精确的表示就不应该困难了。生成“足够接近”的舍入表示实际上要困难得多。 :)

【讨论】:

    【解决方案2】:
    int i = (int) (f * 10000 + 0.5);
    

    【讨论】:

    • 一般来说不是很有帮助的答案(尽管提问者会得到他想要的东西)。这(非常)可能不是他的想法。
    【解决方案3】:

    float 乘以10^x,其中x 是您想要的小数点后的位数,然后转换为int

    【讨论】:

    • 这可行,但假设您事先知道所需的小数位数。
    【解决方案4】:

    如果您不需要对转换后的数字进行任何重复处理,而只是想将数字转换为整数,最简单的方法是使用 sprintf,然后使用 for 循环对小数求和10 条规则。如果您需要数学的“精确精度”,请使用 BCD。以下算法将允许您截断“精确精度”的位数。

    #include "math.h"
    
    long ConvertToScaledInteger (float value, int significantDigits = -1)
    {
        // Note: for 32-bit float, use long return and for double use long long.
    
        if (significantDigits < 1)   // Ditch the '-' and the '.'
            significantDigits += 2;
        else
            ++significantDigits;     // Ditch the '.'
    
        char* floatingPointString = malloc(sizeof (char) * (4 + FLT_MANT_DIG - FLT_MIN_EXP));
        /*< This solution  is for a 32-bit floating point number. Replace FLT 
             with DBL, and it will produce the max number of chars in a 64-bit 
             float string. */
    
        if (significantDigits < 0)   //< Then use all of the float's digits.
        {
            sprintf (floatingPointString , "%f", floatingPointNumber);
        }
        else    //< Then truncate the number of decimal places.
        {
            char decimalPlaceString[9];
            char percentString = "%\0";
            sprintf (decimalPlaceString, "%s%if", percentString , significantDigits);
            sprintf (floatingPointString , decimalPlaceString, value);
        }
    
        int stringLength = strlen (floatingPointString),
            index;
        long returnValue = 0;
        double powerOfTen = 10.0,
            currentValue;
    
        // Find if we have an ending of .0, and backtrack.
        if (floatingPointString[stringLength - 1] == '0' && floatingPointString[stringLength - 2] == '.')
            index = stringLength - 3;
        else
            index = stringLength - 1;
    
        for (; index > 0; --index)
        {
            if (floatingPointString[index] == '.')
                --index;
    
            // Subtract ASCII '0' converts ASCII to integer.
    
            currentValue = (double) floatingPointString[index] - '0';
            returnValue += (long) currentValue * pow (10.0, powerOfTen);
    
            powerOfTen += 1.0;
        }
    
        if (floatingPointString[0] == '-')
            returnValue *= -1;
    
        return returnValue;
    }
    

    【讨论】:

    • 为什么用 C++ 代码回答 C 帖子?推荐移植到C。
    • 4 + FLT_MANT_DIG - FLT_MIN_EXP 太小。见comment
    • "\%%if" 无效的转义序列。
    • 1) 使用int index; ... if (floatingPointString[index - 1],索引未初始化。 2)算法有一定的前景,但有很多错误。建议审查、测试和重新发布。
    • 我明白你在说什么。我修复了错误,但代码没有被调试。字符串的最大大小是正确的。这是一个 32 位浮点数。将 FLT 替换为 DBL 并浮动到两倍,它将正常工作。有关浮动的最大大小,请参阅以下 Stack Overflow 帖子:stackoverflow.com/questions/1701055/…
    【解决方案5】:

    float 数字 f 的整数等效项

    float f=123.456;
    

    可以使用modff()like

    float integral, fractional;
    char str[50], temp[20];
    fractional = modff(f, &integral);
    

    现在integral 有整数部分(如 123.000000),fractional 有小数部分(如 0.456000)。

    如果浮点数(本例中为f)为负数,则integralfractional 均为负数。

    你可以的

    if(fractional<0)
    {
        fractional = -fractional;
    }
    

    解决这个问题。

    现在,

    sprintf(temp, "%g", fractional);
    

    %g 格式说明符将删除尾随零,temp 现在将具有"0.456"

    sprintf(str, "%g%s", integral, temp[1]=='.'?temp+2:"");
    

    temp[1]=='.'? 已完成,因为如果小数部分是 0,则在打印 fractional 时将没有小数点,因为它本来是 0 而不是 0.000000。如果temp 中的第二个字符不是.,则fractional 为零,我们不必费心。

    现在在我们的例子中,str 将是 "123456"。但这是字符串的形式。我们需要将其转换为整数。为此使用strtol()

    long l=strtol(str, NULL, 10);
    if(l>INT_MAX || errno==ERANGE)
    {
        printf("\noverflow");
    }
    else
    {
        printf("\n%d", strtol(str, NULL, 10));
    }
    

    您可以查看strtol()errno的返回值(来自errno.h,检查是否为ERANGE),看看是否发生溢出。

    要查看结果值是否可以存储在int 中,首先将strtol() 返回的值存储在long int 中,看看它是否大于INT_MAX(它在limits.h 中)。

    需要注意的是,结果的准确性将取决于浮点数以二进制表示的准确性。

    【讨论】:

      【解决方案6】:
      #include <stdio.h>
      #include <stdlib.h>
      #include <string.h>
      
      int f2i(float v, int size, int fsize){
          char *buff, *p;
          int ret;
          buff = malloc(sizeof(char)*(size + 2));
          sprintf(buff, "%*.*f", size, fsize, v);
          p = strchr(buff, '.');
          while(*p=*(p+1))p++;
          ret = atoi(buff);
          free(buff);
          return ret;
      }
      
      int main(){
          float f = 123.3443;
      
          printf("%d\n", f2i(f, 7, 4));
      
          f=123.542;
          printf("%d\n", f2i(f, 6, 3));
          return 0;
      }
      

      【讨论】:

        猜你喜欢
        • 2020-02-03
        • 1970-01-01
        • 2013-04-16
        • 2013-10-30
        • 2014-01-30
        • 2015-06-12
        • 2015-06-29
        • 2011-11-17
        • 1970-01-01
        相关资源
        最近更新 更多