最近,我尝试为float (32位)除法创建一个“聪明”的宏,将其舍入到无符号int (32位):
#include "float.h"
#include "stdint.h"
#define DIV_ROUND_UP(x, y) (uint32_t)(((float)(x) / (float)(y)) + (float)(1.0f - FLT_EPSILON))使用FLT_EPSILON,以添加小于1的内容,因此DIV_ROUND_UP(10u,1u)将产生10u,而不是11u。
当DIV_ROUND_UP(10u,1u)返回11u时,我感到非常惊讶。我在实验上验证了我必须使用5* FLT_EPSILON来获得10u,但我仍然不明白为什么。我假设如果我添加小于1的内容,它将在转换为uint32_t时被截断。有人能解释为什么它不被截断吗?为什么5* FLT_EPSILON工作?
编辑:我得到了以下解决方案,它适用于正数:
#define DIV_ROUND_UP(x, y) (((float)(x) / (float)(y)) > (uint32_t)((float)(x) / (float)(y)) ? \
(uint32_t)((float)(x) / (float)(y)) + 1u : \
(uint32_t)((float)(x) / (float)(y)))发布于 2022-08-04 13:28:28
FLT_EPSILON是1到下一个可表示数字之间的距离。在二进制浮点格式中,1和2之间的可表示数字间隔为距离FLT_EPSILON,2和4之间的数字是距离的两倍,4和8之间的数字是FLT_EPSILON的4倍,8和16之间的数字是8倍FLT_EPSILON的间隔,等等。这是因为浮点数被表示为一个有意义的数字,并乘以一个比例.FLT_EPSILON是数字1所使用的尺度上的数字之间的距离,因此其他尺度上的数字之间的距离与它们的尺度成正比。
在10处,可表示数之间的距离为8·FLT_EPSILON。当将1.0f - FLT_EPSILON加到10中时,实数算术结果将是11−FLT_EPSILON.但是这个数字是不可表示的,因为可表示的数字分开8·FLT_EPSILON;单个FLT_EPSILON相差太小。与11 FLT_EPSILON最接近的可表示数是11和11−8·FLT_EPSILON。生成结果的默认规则是在两个方向上生成最接近的可表示数字。由于11 FLT_EPSILON比11−8·FLT_EPSILON更接近11−,因此产生11。
当添加1.0f - 5 * FLT_EPSILON时,实数结果为11−5·FLT_EPSILON.这比11 is更接近于11 FLT_EPSILON8·FLT_EPSILON,所以11−8·FLT_EPSILON是由减法得到的。然后将其转换为uint32_t截断,生成10。
要在许多情况下获得浮点除法,只需使用ceil:#define DIV_ROUND_UP(x, y) ((uint32_t) ceil((float) (x) / (float) (y)))。在某些情况下,如果x/y略高于整数,那么浮点除法在ceil函数操作之前会舍入到该整数,则这种方法可能不起作用。
https://stackoverflow.com/questions/73236560
复制相似问题