【问题标题】:Float (or double) to decimal fixed point integer浮点数(或双精度数)转十进制定点整数
【发布时间】:2015-02-08 19:07:06
【问题描述】:

我想用 C 语言将浮点数或双精度数转换为十进制定点整数。考虑到 C 语言的规范,我正在为这个问题寻找最合适(稳健)的解决方案。

例如,问题是这样的:

double d = 15.6;
int    i;
...
i = (int)(d * 10.0); /* I am excepting getting 156 in 'i' */

据我所知,标准的相关元素(使用 C99 标准,ISO/IEC 9899:TC3,参见this question 下载):

  • 6.3.1.4 / 1:当实浮点类型的有限值转换为_Bool以外的整数类型时,小数部分被丢弃(即,该值被截断为零)。 (...)
  • 6.4.4.2 / 3:(...) 对于十进制浮点常量,(...) 结果是最接近的可表示值,或者是紧邻最近的可表示值的较大或较小的可表示值,在一种实现定义的方式。 (...)

示例中的 15.6 在 IEEE double 中没有得到精确的表示,所以除了 d 之外,我会得到略高于或略低于的东西。问题在于“略低于”部分,然后示例中的i 不会得到例外结果(而是会得到155)。

我的明显看法是这样的(仅考虑零或正值):

i = (int)(d * 10.0 + 0.5);

至少如果我正确解释了 C 标准。我问是因为由于“实现定义的”行为,人们可能会遇到一致的结果,而实际上其他一些实现可能会破坏程序,因此反复试验并不是找到合适解决方案的充分方法。

特别是the following question 涉及到这个问题,我认为这个问题的答案不正确。

【问题讨论】:

  • C 标准不强制使用 IEEE 浮点。从理论上讲,d 可能被表示为像15.65 这样的疯狂数字。随着d 变大,这变得更有可能。我想说d 已经错了,你应该从一开始就使用任意精度库。
  • @Kevin:我刚才提到了 IEEE,因为即使在 IEEE 中,这个例子也说明了问题。如果按照 C 标准的措辞加上严格的“重型火炮”,当然你很快就会有一个大洞。任意精度库可能并不总是可行的,例如考虑嵌入式目标。
  • 好吧,那么你必须决定你更看重哪一个:性能还是正确性?
  • 我怀疑 (void) fesetround(FE_DOWNWARD); 后面跟着 i = (int) nearbyint(d);,但我几乎不是浮点专家,所以我不敢将这个作为答案发布。
  • @Kevin:C 语言的浮点数和双精度数不应该是那么糟糕!还是他们? (因此在最坏情况下使用标准 C 无法解决此问题)

标签: c floating-point


【解决方案1】:

C99 专门为此目的指定了一个舍入函数。使用它,然后转换为 int。

更新:对于 C89,您可以尝试

double y = floor(x);
double z = x == y ? x : floor(2.0*x-y);

这应该与 C99 round 相同,除了小数部分等于 0.5 的负数将向上舍入(如 Java),并且零可能被错误地签名(这是基于类似的技巧 due to Arch Robinson )。

【讨论】:

  • 呵呵,找到了(我的意思是在标准中)!我是一个在嵌入式领域工作的严格的 C89 人,我什至没有想到 C99 可以拥有这个。我接受它,因为我在帖子中提到了 C99 标准,所以我真的不能说这个解决方案有什么问题。不过,如果最终将答案编辑为包含符合 C89 的解决方案,那就太好了。
  • 很有趣(这篇文章也是),而且看起来很实用,C89 可以。顺便说一句z = floor(x + 0.5) 为什么还不够? C89(及以上任何内容)定义 floor 以返回不大于其操作数的最大整数,因此它也应该适用于负数。 Arch Robinson 使用的是trunc,它对于负数趋向于零(如果我从标准中提高 6.3.1.4/1,类似于我的看法 - 这是另一个故事,C99 中 fesetround 的存在说明了这一点标准的气味假)。
  • floor(x+0.5) 可能足以满足您的情况,但一般情况下并非如此:导致问题的两种情况是x = 0.499999999999999940.5 下面的浮点数),其中加一半将舍入为1,以及2^52和2^53之间的绝对值的奇数,其中加一半将舍入到下一个偶数。
  • 另外,我似乎记得 C89 没有指定 C99 中的截断行为(即它允许选择上方或下方的整数),但您应该仔细检查。
  • 截断存在于我的C89草案中,我什至以此为基础形成了问题。我没有参考 C89 草案,因为它(可能?)不像我引用的 C99 版本那么容易找到。不错的小细节,虽然在你之前的帖子中。正如我所看到的 FP,在处理 FP 时不应该依赖任何精确的东西,四舍五入也像“我需要合理地大约 1 的值才能四舍五入到整数 1”:毕竟任何 FP 计算都会带来不准确的累积。但是,当它用于编译器设计时,这是另一回事。
猜你喜欢
  • 2014-02-07
  • 1970-01-01
  • 1970-01-01
  • 2014-01-30
  • 1970-01-01
  • 2014-08-14
  • 2013-12-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多