这个在 C 中浮动的程序真的有未定义的行为吗?

Does this program that floors float in C really have undefined behavior?

所以程序基本上是这样的:

float k=0.01345;
k *= 100;
k = (int)k ;
k /= (float) 100

一点背景:出于某种我真的不记得的原因,一位同事需要将浮点值下限到小数点后 2 位。我建议了这种方法(因为他说他不能使用 printf,我又记不太清楚为什么了)。但后来另一位同事说这种方法不可信,可能会出现未定义的行为。这是真的吗?

我知道如果浮点数大于 INT_MAX,将 float 转换为 int 会出现未定义的行为,但这不会发生在这个函数中,因为它使用介于10000 和 0.

您的方法存在一些问题:

  • k 未初始化,但我想你只是写了 float k; 来指定 k.
  • 的类型
  • k = int(k); 不是有效的 C 代码。你可以写 k = (int)k; 但使用 floorf() 函数会更可靠(或者 floor() 如果你的系统没有 float 特定版本):k = floorf(k);
  • 您没有 舍入 值,而是将其截断为 0。这是有问题的,因为 1.999 之类的值将转换为 1.99 而不是 2.00。您应该改用 roundf()

这是修改后的版本:

float round2(float k) {
    return roundf(k * 100.0F) / 100.0F;
}

但是请注意,float 不能准确表示 0.01 的大多数倍数。如果您处理货币金额并希望避免因精度错误而导致的差一错误,请使用 _Decimal32_Decimal64 等小数类型,或使用整数类型来处理整数美分。

假设 k 是一个范围内的、可修改的 float 变量,其定义明确的值在 0 - 10000 范围内,如指定的那样,此代码的行为 ...

k *= 100;
k = (int)k ;
k /= (float) 100;

... 在 C 中定义明确。它生成将 k 的十进制值截断为两位小数的结果的近似值。

但是,具有两位有效小数位的精确十进制数很少可以用二进制浮点数精确表示,因此一般来说,近似值是您可以期望的最佳结果。请特别注意它不一定稳定:根据有效的舍入模式,如果您再次将过程应用于结果,在某些情况下您可能会得到略有不同的输出。

Does this program that floors float in C really have undefined behavior?

是的,但你已经知道了:

I know that converting float to int has undefined behavior if the float is greater than INT_MAX, but this wouldn't occur

有了这个约束:不,呈现的程序有定义的行为。

--- 请注意,当 intINT_MAX = 32767 有 16 位时,10000*100 将比 INT_MAX 大得多。可以肯定的是,确保输入低于 INT_MAX/100.