2011-11-02 61 views
3

假设我有一些代码,如:我可以信任ceil()的结果的实数转换吗?

float a, b = ...; // both positive 
int s1 = ceil(sqrt(a/b)); 
int s2 = ceil(sqrt(a/b)) + 0.1; 

有没有可能是s1 != s2?我的关注是当a/b是一个完美的广场。例如,可能是a=100.0b=4.0,那么ceil的输出应该是5.00000,但是如果代之以是4.99999

相似问题:有没有机会100.0/4.0评估说5.00001,然后ceil将它舍入到6.00000

我宁愿这样做在整数数学,但sqrt有点螺丝计划。

编辑:关于如何更好地实现这一点的建议,也将不胜感激!该ab值是整数值,所以实际的代码更像是:ceil(sqrt(float(a)/b))

编辑:基于levis501的回答,我想我会做到这一点:

float a, b = ...; // both positive 
int s = sqrt(a/b); 
while (s*s*b < a) ++s; 

谢谢大家!

+2

这些计算是什么?例如,你可以将'sqrt(x) GManNickG

+1

不幸的是,即使“a/b”是一个完美的正方形,'s1!= s2'也是很可能的。浮点运算是非常不准确的,所以你获得'5.00000'(或者任何你想要的数字)的机会都很渺茫。 – ssube

+0

@peachykeen即使我们正在处理整数数字? '5.0'可以完美地表示为一个浮点数,'100.0'和'20.0'也是如此。 – glglgl

回答

2

你可能想为你的情况写一个明确的函数。例如为:

/* return the smallest positive integer whose square is at least x */ 
int isqrt(double x) { 
    int y1 = ceil(sqrt(x)); 
    int y2 = y1 - 1; 
    if ((y2 * y2) >= x) return y2; 
    return y1; 
} 

这将处理奇数情况下的比率的平方根a/bdouble的精度内。

+0

我喜欢这种方法。它证明是正确的,不必知道或依赖浮点实现细节。 – Dan

-1

是的,这完全有可能是s1 != s2。但是,为什么这是一个问题? 这似乎很自然,s1 != (s1 + 0.1)

顺便说一句,如果你希望有5.00001四舍五入到5.00000而不是6.00000,使用rint代替ceil


并回答实际问题(在您的评论) - 你可以使用sqrt得到一个起点,然后随便找个使用整数运算正确的广场。

int min_dimension_greater_than(int items, int buckets) 
{ 
    double target = double(items)/buckets; 
    int min_square = ceil(target); 
    int dim = floor(sqrt(target)); 
    int square = dim * dim; 
    while (square < min_square) { 
     seed += 1; 
     square = dim * dim; 
    } 
    return dim; 
} 

是的,这可以改善很多,它只是一个快速的草图。

+2

s1和s2都是整数。铸造截断小数点 – levis501

+1

请注意,s1,s2是整数。我会研究rint,不知道它是否可用于我需要支持的所有平台。 – Dan

+0

@Dan:如果你没有'rint',那么'floor(x + 0.5f)'会给出相同的结果。 –

6

我不认为这是可能的。不管sqrt(a/b)值的,它所产生的是,我们使用作为一些值N:

int s1 = ceil(N); 
int s2 = ceil(N) + 0.1; 

由于小区总是产生的整数值(虽然表示为双),我们将始终有一些值X,为其中第一个产生X.0,第二个产生X.1。转换为int将始终截断.1,所以两者都将导致X

如果X太大以至于X.1溢出了double的范围,看起来似乎会有一个异常。我不明白这可能是可能的。除了接近于0(其中溢出不是问题),数字的平方根总是比输入数字小。因此,在ceil(N)+0.1可能溢出之前,作为sqrt(a/b)中的输入的a/b必须已经溢出。

+0

我同意我们应该期待信任'ceil',但我无法找到C标准中的哪里(如果有的话),它实际上是在讨论'math.h'中函数的准确性。假设0.5ulp的准确度,'ceil' *具有*返回一个整数,并且执行失败将是非常不合理的。但是我们知道,'cos'不能保证是0.5ulp的准确值(或者'sqrt',我认为这可以回答第二个问题),那么为什么'ceil'应该是?除了因为明显的实现是屏蔽尾数的一些位(如果那些位不是0,加1),我的意思是。 –

+0

我想这取决于我们是否读“计算最小整数值不小于x”意思是“保证返回的值是一个整数,等于......”或者意思是“确切的数学结果是...,但返回的价值受到通常不准确的影响“。 –

+0

@SteveJessop:实际上,你唯一一次遇到问题的机会是64位int。对于典型的32位int和具有53位尾数的double,你在做任何困难转换double到精确的整数值之前都会溢出int。 –

1

浮点数的平等确实是一个问题,但恕我直言,不是如果我们处理整数。

如果你有100.0/4.0的情况,它应该完全评估为25.0,因为25.0可以精确地表示为浮点数,例如与之相反。 25.1

-2

s1将始终等于s2。

C和C++标准对数学例程的准确性没有太多的说明。从字面上看,标准是不可能实现的,因为C标准说sqrt(x)返回x的平方根,但是二的平方根不能精确地用浮点数表示。

具有良好的性能实现例程总是返回正确舍入的结果(舍入到最接近的模式,这意味着结果是可表示的浮点数最接近的确切结果,有利于解决关系低零位)是一个很难研究的问题。好的数学库的目标精度小于1 ULP(因此返回两个最接近的可表示数字中的一个),也许稍微多于.5 ULP。 (ULP是最小精度的单位,低位的值在指数字段中给出了特定的值。)一些数学库可能会比这更差。您将不得不询问供应商或查看文档以获取更多信息。

所以sqrt可能会稍微偏离。如果确切的平方根是一个整数(在整数可精确表示浮点范围内),并且库保证错误小于1 ULP,那么sqrt的结果必须完全正确,因为除了确切的结果是至少1 ULP了。同样,如果库保证误差小于1 ULP,则ceil必须返回精确结果,同样因为精确结果是可表示的,并且任何其他结果至少会有1 ULP。此外,ceil的性质使我可以期望任何合理的数学库总是返回一个整数,即使库的其余部分不是高质量的。对于溢出情况,如果ceil(x)超出了所有整数都可以精确表示的范围,那么ceil(x)+ .1比其他任何可表示的数字更接近ceil(x),所以在执行浮点标准(IEEE 754)的任何系统中,向ceil(x)添加.1的舍入结果应该是ceil(x)。假设您处于默认的四舍五入模式,这是舍入到最近的。可以将舍入模式更改为round-toward-infinity,这可能会导致ceil(x)+。1为比ceil(x)更高的整数。

相关问题