【问题标题】:Fast rounding float on three digits precision in CC中三位精度的快速舍入浮点数
【发布时间】:2015-02-07 16:07:15
【问题描述】:

我看过这段代码:

(int)(num < 0 ? (num - 0.5) : (num + 0.5))

(How to round floating point numbers to the nearest integer in C?) 进行舍入,但我需要在点后使用浮点数和精度来表示三位数字。 例子: 254.450 应该四舍五入到 255。 254.432 应向下舍入为 254 254.448 应向下舍入为 254 等等。

注意:这就是我所说的“3 位”点后的粗体数字。

我相信它应该比 roundf() 更快,因为当我需要计算轮数时,我使用了数十万轮。你有一些提示如何做到这一点?我试图搜索roundf的来源,但没有找到。

注意:我需要它用于 RGB2HSV 转换功能,所以我认为 3 位数应该足够了。我使用正数。

【问题讨论】:

  • 您要求“该点后的三位数字”,但您提供的所有三个示例都在该点后向下舍入到零位。什么?
  • 我认为你误解了四舍五入。为什么254.450会四舍五入到255
  • 圆形 0.45 到 0.5 和 0.5 到 1 所以 254 + 1 是 255
  • 只需将公式中的 0.5 更改为其他值即可调整舍入截止点
  • 254.450 通常不能精确地表示为 float。最接近的float254.4499969482421875000...,它根据您的方法四舍五入到254.4 --> 254.0

标签: c rounding


【解决方案1】:

“它应该比 roundf() 更快”只能通过分析各种方法来验证。

要四舍五入到 0 位(四舍五入到最接近的整数),请使用 roundf()

float f;
float f_rounded3 = roundf(f);

要使用 float 舍入到 3 个位置,请使用 round()

round 函数将其参数四舍五入为浮点格式中最接近的整数值,从零开始舍入一半大小写,而不管当前舍入方向如何。

#include <math.h>

float f;
float f_rounded3 = round(f * 1000.0)/1000.0;

代码故意使用double的中间类型,否则代码代码使用缩小范围:

float f_rounded3 = roundf(f * 1000.0f)/1000.0f;

如果代码在使用roundf() 或各种测试将254.450 舍入到255.0 时遇到问题,可能是因为该值不是 254.450,而是float 接近就像 254.4499969 舍入到 254. 使用二进制格式的典型 FP 和 254.450 不完全可表示。

【讨论】:

  • roundf(254.450) = 254 是一个正确的结果(并且不会出现精度问题)。我不明白为什么 OP 要将其舍入到 255。
  • @Martin R OP 似乎逐步舍入并引发successive rounding 的问题。最好使用roundf()。这就是它在图书馆的原因。
  • 所以 roundf 没有像我预期的那样四舍五入。如果你有数字 0.449,那么首先你应该四舍五入最后一个数字:0.450,然后你应该四舍五入第二个数字:0.500,然后你应该四舍五入到 1...这就是我想要的
  • @user1141649 确实,roundf() 没有像您期望的那样四舍五入。但是roundf() 正在按照绝大多数人的预期进行。您的方法虽然可能对您的目的有效,但与数学和计算机科学背道而驰。看来您希望任何小数值 0.4445000(或者可能是 0.445000)或更高的四舍五入,否则向下舍入。在这种情况下,将 +/- (0.5000-0.4445) 添加到您的值并继续使用roundf()
  • @user1141649 使用默认舍入的 PHP round() 会像这个答案一样执行,当然不会像您建议的那样舍入。实现有所不同,但等效功能是缩放(* 1000 3 个位置)数字,四舍五入到最接近的整数(关系方向取决于模式),然后缩小(/1000 3 个位置。)
【解决方案2】:

你可以使用双重转换float->string->float,而第一次转换在point之后是3位数:

  sprintf(tmpStr, "%.3f", num);

【讨论】:

  • 我不想调用任何函数来四舍五入到三个地方
  • 好的。使用chux的方法
【解决方案3】:

这对我有用

#include <stdio.h>


int main(int ac, char**av)                                                                    
{                                                                                             
  float val = 254.449f;                                                                       
  float val2 = 254.450f;                                                                      
  int res = (int)(val < 0 ? (val - 0.55f) : (val + 0.55f));                                   
  int res2 = (int)(val2 < 0 ? (val2 - 0.55f) : (val2 + 0.55f));                               
  printf("%f %d %d\n", val, res, res2);                                                       
  return 0;                                                                                   
}                                                                                             

输出:254.449005 254 255

要提高精度,只需在 0.55f 中添加您想要的任何 5,例如 0.555f、0.5555f 等

【讨论】:

  • 我测试了你的代码,但我的结果是254.449005 254 254
  • 这是因为浮点难以实现,因为您可以看到 val 不完全是 254.449f,因此您可以使用 0.56f 进行测试,但如果精度很高,则无法满足您的任何需求。
【解决方案4】:

我想要这样的东西:

float num = 254.454300;
float precision=10;
float p = 10*precision;
num = (int)(num  * p + 0.5) / p ;

但结果会不准确(有错误) - 我的 x86 机器给我这个结果:254.449997

【讨论】:

    【解决方案5】:

    对 int 进行类型转换并使用 float 进行操作(另一种类型转换)很慢。使用零位四舍五入(整数)操作浮点数,返回浮点数进行n位四舍五入。

    round_1( x ) = round_0( 10*x )/10

    round_2( x ) = round_0( 100*x )/100

    round_3( x ) = round_0( 1000*x )/1000

    round_n( x ) = round_0( 10^n*x )/10^n

    为了更快的零位数轮转浮点类型,你可以这样做:

    inline float round( float x ){
        x += 12582912 ; // 3*2^22
        x -= 12582912 ;
        return x ;
    }
    

    如果不简化操作,如果|x|

    inline long double round( long double x ){
        x += ( sizeof(x)==8 ? 6755399441055744.0 : 27670116110564327424.0 ) ; // 3*2^51 or 3*2^63
        x -= ( sizeof(x)==8 ? 6755399441055744.0 : 27670116110564327424.0 ) ;
        return x ;
    }
    

    它在这里工作(长双)。

    【讨论】:

      猜你喜欢
      • 2015-06-12
      • 1970-01-01
      • 1970-01-01
      • 2021-05-02
      • 1970-01-01
      • 2011-12-04
      • 1970-01-01
      • 2010-09-14
      • 2011-02-28
      相关资源
      最近更新 更多