【问题标题】:How is the standarized way to calculate float with integers?如何用整数计算浮点数的标准化方法?
【发布时间】:2019-06-26 03:51:23
【问题描述】:

你们中有人知道在 C 中如何计算吗?

uint8_t samplerate = 200;
uint8_t Result;
Result = 0.5 * samplerate;

现在,问题是 0.5 是浮点数,samplerate 是整数。 Result 可以是 0,因为 0.5 被转换为整数,因此四舍五入为 0 (Result = 0 * 200 = 0)。或者 Result 可能是 100,因为编译器首先看到 0.5 并将 samplerate 转换为浮点数 (Result = 0.5 * 200 = 100)。

编译器将如何处理这些计算是否有一种标准化的方式? 我的意思是编译器会先查看最左边的变量(在本例中为 0.5)并将另一个变量转换为此,还是会查看最右边的变量(samplerate)并将其他变量转换为此?

我知道如何解决这个问题,但我正在寻找一个通用的答案,如果这是 C 标准化的,它将如何计算这样的方程?

【问题讨论】:

  • 请阅读this implicit conversion reference。它会告诉您0.5不会 转换为整数,而是相反,samplerate 将转换为double
  • @Someprogrammerdude,恐怕您正在使用具有(不同)转换规则的 c++ 参考作为参考。由于可能存在一个参数构造函数,C++ 中的自动隐式转换规则比 C 中的要复杂得多(它们是动态的、运行时的),并且您发布的示例无效。
  • @LuisColorado 该站点可能被称为“cppreference”,但该站点包含CC++ 的引用。我提供的链接指向 C 转换参考
  • @Someprogrammerdude,我为我的错误道歉。你是对的。

标签: c type-conversion type-promotion


【解决方案1】:

当各种类型的数值组合在一个表达式中时,它们会受到通常的算术转换的约束,这是一组规定应该将哪个操作数转换为什么类型的规则。

这些转换在 C 标准的第 6.3.1.8 节中有详细说明:

许多期望算术类型的操作数的运算符导致 转换和产生结果类型以类似的方式。目的是 确定操作数和结果的通用实数类型。为了 指定操作数,每个操作数都被转换,不改变类型 域,到一个类型,其对应的真实类型是 常见的实型。除非另有明确说明,否则 common real type 也是对应的 real type 结果,其类型域是操作数的类型域 如果它们相同,则复杂。这个图案是 称为通常的算术转换:

  • 首先,如果任一操作数对应的实数类型为 long double ,则将另一操作数转换为对应实数类型为 long 的类型,而不改变类型域 双倍。
  • 否则,如果任一操作数的对应实数类型为 double ,则将另一个操作数转换为对应实数类型为 双倍。
  • 否则,如果任一操作数的对应实类型为 float ,则另一个操作数将在不改变类型域的情况下转换为对应实类型为 浮动。
  • 否则,将在两个操作数上执行整数提升。然后将以下规则应用于提升的 操作数:
    • 如果两个操作数的类型相同,则无需进一步 需要转换。
    • 否则,如果两个操作数都已签名 整数类型或两者都有无符号整数类型,操作数 用较小整数转换的类型进行秩转换 到具有更高等级的操作数的类型。
    • 否则,如果 具有无符号整数类型的操作数具有更大的等级或 等于另一个操作数的类型的等级,然后 带符号整数类型的操作数转换为类型 无符号整数类型的操作数。
    • 否则,如果 带符号整数类型的操作数的类型可以表示所有 具有无符号整数类型的操作数的类型的值,然后 无符号整数类型的操作数转换为类型 带符号整数类型的操作数。
    • 否则,两个操作数都转换为无符号整数类型 对应带符号整数类型的操作数的类型。

特别注意粗体段落,这适用于您的情况。

浮点常量0.5的类型为double,因此其他操作数的值转换为double类型,乘法运算符*的结果为double类型。然后将此结果分配回uint8_t 类型的变量,因此double 的值将转换为此类型进行分配。

所以在这种情况下,Result 的值为 100。

【讨论】:

    【解决方案2】:

    是的,有一个标准。在这种情况下,表达式中的数字会自动转换为更宽的类型(占用更多字节的类型),因此您的表达式将按如下方式计算:

    (0.5: double) * (0: uint8_t) => (0.5: double) * (0.0: double) == (0.0: double)
    uint8_t Result = (0.0: double) => (0: uint8_t) // this is a forced cast, because Result is of type uint8_t
    

    doubleuint8_t 宽,所以 (0: uint8_t) 加宽(0.0: double)。此转换不会丢失信息,因为double 占用了足够的空间来容纳存储在uint8_t 中的所有数据。

    【讨论】:

    • 为了挑剔,long long 可能占用比float 更多的字节,但如果另一个操作数属于该类型,它将转换为float
    • 将此描述为扩大问题既具有误导性又不完整。在这种情况下,uint8_t 被转换为double 用于计算,因为另一个操作数对应的实类型是double,不多也不少。如果浮动操作数是 0.5ffloat,而另一个是 long long,则后者将转换为 float 以进行操作,即使 long long 可能比 float 更宽(并且不管它实际上是否更宽)。
    • @Lundin,为了挑剔,long long 可能占用至少long 一样多的字节。这并不意味着long long 一定大于float。可以相同大小,甚至更短。恐怕你不是挑剔,只是弄错了。
    • @LuisColorado 在几乎所有已知的现实世界实现中,long long 是 8 个字节,float 是 4 个字节。然而 long long 将被“提升”为浮动。
    • @Lundin,从整数值(任何大小,甚至 char)到 double 值的 C 中的提升。恐怕你已经被告知了这件事。无论如何,long long 的促销总是会丢失一些信息,因为long long 有 64 个有效位,而double 只有 52 个。有什么问题?我认为您坚持要寻找更广泛的浮点数……而事实并非如此。标准表示与浮点混合的整数提升为浮点,即使这意味着失去一些精度(你几乎总是这样做)
    【解决方案3】:

    是的,当然这是由标准控制的,这里没有不确定性。

    基本上整数会被提升为double(因为0.5的类型是double,它不是float)并且计算会在那里发生,然后结果将被截断回@987654325 @。通常,编译器会因精度损失而对您大喊大叫。如果没有,请根据需要添加更多警告选项。

    【讨论】:

    • 另请注意,将小于或等于 -1 的 double 转换为无符号类型的行为是未定义的。
    • 但是这里的行为不是转换小于-1的double,它确实是100.0,并且在@987654329的IEEE-752内部表示的情况下甚至允许精确表示@s。您发表评论的原意是什么? double 这里的结果是 100.0 所以它可以转换为 unsigned 并适合它的表示(它小于 255.0
    猜你喜欢
    • 1970-01-01
    • 2013-04-01
    • 2018-03-22
    • 1970-01-01
    • 2017-06-08
    • 1970-01-01
    • 1970-01-01
    • 2015-09-09
    • 1970-01-01
    相关资源
    最近更新 更多