【问题标题】:Converting unsigned to double to unsigned without loss of precision在不损失精度的情况下将 unsigned 转换为 double 到 unsigned
【发布时间】:2013-12-07 12:23:24
【问题描述】:

将整数值转换为浮点值并再次返回与原始整数值相同吗?

例如:

unsigned x = 42;
double y = x;
unsigned z = y;

假设编译器不优化浮点转换,x == z 是否总是评估为 true

我怀疑浮点转换中的任何表示错误将始终是值的增加。因此,当浮点值被转换回整数值时,该值被截断,这总是导致原始整数值。

我的假设正确吗?

【问题讨论】:

  • 不,转化不会始终保持相同的价值或增加它。如果源值完全可表示,则没有错误。如果源值不能精确表示,则将其四舍五入到 最近 可表示值(在通常的默认舍入模式下),在其有效位的最低有效位(分数浮点格式的一部分)。所以舍入有时会向上,有时会向下。
  • @EricPostpischil 所以对于大于 253 的整数,可能会出现向下舍入的情况,这样当转换回整数时,截断的值小于原来的值(即x == z 可能导致0 整数大于253)?
  • 是的,Pascal Cuoq 在his answer 中举了一个例子,253+1。请注意,当值转换回整数时,它不会被截断,因为它仍然是整数。例如,将 253+1 转换为 double 得到 253。将其转换回 64 位整数格式会产生 253。这第二次转换是准确的;它不会截断或舍入,因为该值可以以新的目标格式(64 位整数)精确表示。
  • 也有可能将大整数转换为双整数,导致最终值大于原始值。最常见的双舍入模式,即四舍五入到最接近,平数到偶数,被设计为同样频繁地向上和向下舍入。

标签: c floating-point c99 ieee-754 floating-point-conversion


【解决方案1】:

假设 double 的 IEEE 754 双精度格式,对于 x 的所有值,表达式 x == z 将计算为 1,最多为 253。例如,如果您的编译器提供 32 位 unsigned int,这意味着所有可能的 x 值。

您已编辑问题以询问从整数到浮点数的转换。在大多数 C 实现中,此转换根据 FPU 舍入模式进行舍入,默认情况下舍入到最近偶数。那里从浮点数到整数的转换存在不对称性(正如您所指出的,从浮点数到整数的转换总是被截断)。

但是,从整数到浮点数的转换中的任何错误并不意味着您得到了一个没有小数部分的部分,而是您完全得到了错误的整数。例如整数 253+1 被转换为代表 253double。因此,即使从浮点数到整数的转换总是向上取整,从浮点数到整数的转换也会被截断。

整数到浮点数转换的舍入误差可能大于一:整数5555555555555555555,当转换为double时,会舍入为5555555555555555328,这恰好比二进制更简单的表示前者。一半的时间,四舍五入向上:例如5555555555555555855被四舍五入为5555555555555556352

【讨论】:

  • 受C 2011 (N1570) 6.3.1.4 2支持:“当整数类型的值转换为真正的浮点类型时,如果被转换的值可以在新类型中精确表示,则不变。” 1:“当实浮点类型的有限值转换为 _Bool 以外的整数类型时,小数部分被丢弃(即,该值被截断为零)。”
  • @chux: 254-1 有 54 位。例如,24-1 是二进制 1111。
  • @chux: 21-1 是二进制 1;它有 1 位。 22-1为二进制11;它有 2 位。 23-1是二进制111;它有 3 位。 24-1为二进制1111;它有 4 位。 25-1是二进制11111;它有 5 位……254-1 有 54 位。在 254-1 中,从 253 位置到 2**0 位置的每个位置的位都是 1。即 54 个位置。
  • @PascalCuoq 当你提到 FPU 舍入模式时,我有点困惑;我认为这只适用于nearbyint 函数。你是说某些实现不会截断非整数浮点值,而是在使用float 变量初始化int 时使用当前的FPU 舍入模式?
  • @VilhelmGray 从 float 到 int 的转换总是被截断:这就是标准所说的,这就是故事的结尾。当值完全可表示时,从 int 到 float 的转换产生完全相同的值(参见 Eric 挖出的标准引用),并且当要转换的整数值不是时,实现定义的上限值或下限值的选择在目标类型中完全可以表示。大多数 C 实现将选择定义为:“根据当前设置的 FPU 舍入模式”。
【解决方案2】:

如果double 遵循 IEEE-754(如您的标签所示),则任何不超过 253 的整数都可以精确表示为双精度浮点数。因此,假设 int 是 32 位,是的,您可以将 unsigned 转换为 double 而不会损失精度。

【讨论】:

    【解决方案3】:

    假设您的浮点双精度类型是 64 位 IEEE754 类型。 (C 标准不坚持这一点,但它是您标记的内容)。

    这取决于您的unsigned int 的大小。如果是 32 位,则可以,如果是 64 位,则不一定。 (截止在第 53 位:253 + 1 是不能在 IEEE 浮点 double 中精确表示的最小正数。

    在 32 位平台上,答案始终是肯定的。

    在 64 位平台上,它取决于编译器。在 LP64 和 LLP64 中,unsigned int 是 32 位,但在 ILP64 中是 64 位。 (请注意,Win64 使用 LLP64,long 也设置为 32 位。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-10-08
      • 2012-12-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多