【问题标题】:Why bother using a float / double literal when not needed?为什么在不需要时使用浮点/双字面?
【发布时间】:2014-05-27 20:23:45
【问题描述】:

当您需要整数值并且整数文字无论如何都会隐式转换为双精度/浮点时,为什么要使用双精度或浮点文字?当需要小数值时,为什么还要添加f(以制作浮点文字),无论如何都会将双精度转换为浮点数?

比如我经常看到类似下面的代码

float foo = 3.0f;
double bar = 5.0;
// And, unfortunately, even
double baz = 7.0f;

void quux(float foo) {
     ...
}

...

quux(7.0f);

但据我所知,这些等价于

float foo = 3;
// or
// float foo = 3.0;
double bar = 5;
double baz = 7;
quux(9);

如果您使用重载语言(c++,java),我可以理解方法调用,如果函数重载(或将来),它实际上可以产生功能上的差异,但我更担心使用没有重载的 C(以及在较小程度上是 Objective-C)。

那么有什么理由打扰额外的小数和/或f?特别是在初始化的情况下,声明的类型就在那里

【问题讨论】:

  • 我很感激我的开发人员同事明确说明他们的类型,因为这告诉我他们在编写代码时考虑了类型。否则,看起来他们可能很懒惰并且没有思考:一时兴起将intsfloatsdoubles 混合在一起。简而言之,它让我作为一名代码审查员,相信他们会尽职尽责。
  • 当变量的含义是浮点数时,我会这样做。即使我目前将它与整数值一起使用,变量的声明也会提醒我变量应该代表什么。你说得对,不需要额外的.0,对我来说只是一个额外的提醒。
  • @abelenky 当您进行代码审查并看到f 时,请务必仔细检查他们是否真的需要浮点数,而不是双精度数。部分原因是找到存储在双变量中的浮点文字。碰巧与这个特定值无关,但仅仅因为他们添加了f 并不一定意味着他们关注类型。
  • @Jongware 该问题的公认答案对于任何非古代编译器都是错误的。

标签: c implicit-conversion floating-point-conversion


【解决方案1】:

很多人都经历了艰难的学习

double x = 1 / 3;

没有按预期工作。所以他们(包括我自己)通过使用浮点文字而不是依赖隐式转换来进行防御性编程。

【讨论】:

  • 我想这是根本原因,但我仍然认为这是反应过度。
  • @Kevin 我猜这取决于你的理念。你认为总是在if 语句上使用大括号是过度反应吗?
  • @TavianBarnes:大括号是否是强制性的,恕我直言,大括号是单独放置在一行上还是放置在它们所附加的控制语句的末尾。如果哪些控制语句有左大括号而哪些没有,那么哪些控制语句需要右大括号在视觉上很明显。如果很难看到左大括号,那么有些语句使用大括号而有些不使用大括号将很难判断哪些语句不正确匹配。
  • @supercat 这当然是一个有效的编码标准。但是需要大括号以避免if (x) y; z; 错误的策略也是一个完全合理的标准。 (好吧,if (x) { y; } else if (z) { q; } 通常是例外,因为else { if (z) { q; } } 很丑。)
  • 您的评论没有显示换行符,因此很难说出您对它们的假设。如果开括号自己排成一行,那么当开括号缺少闭括号时,这是显而易见的。如果语句从不缩进,除非左大括号在前一行,那么任何没有右大括号的缩进都会很明显。单独的大括号行允许单个语句 if 占用两行,但两个语句 if 需要五行;前大括号样式意味着两个语句 if 需要四行(vs 5),但单语句 if 需要三行(vs 2)。
【解决方案2】:

C 没有重载,但它有一个叫做可变参数函数的东西。这就是.0 的重要性所在。

void Test( int n , ... )
{
    va_list list ;
    va_start( list , n ) ;
    double d = va_arg( list , double ) ;
    ...
}

调用函数而不指定数字是双精度数将导致未定义的行为,因为 va_arg 宏会将变量内存解释为双精度数,而实际上它是一个整数。

Test( 1 , 3 ) ; 必须是 Test( 1 , 3.0 ) ;


但你可能会说;我永远不会写可变参数函数,那何必呢?

printf( 和 family ) 是可变参数函数。

调用,应该产生一个警告:

printf("%lf" , 3 ) ;   //will cause undefined behavior

但根据警告级别、编译器以及忘记包含正确的标头,您将不会收到任何警告。

切换类型也会出现问题:

printf("%d" , 3.0 ) ;    //undefined behaviour

【讨论】:

  • 这不能解释 OP 提供的任何示例。事实上,调用printf只需要格式字符串和参数类型匹配,所以如果你使用正确的格式字符串,没有理由使用0.0f5.0
  • @user4815162342 否,如果省略 .0,并指定浮点类型,则会得到 UB。 + 我的回答解释了 那么有什么理由打扰额外的小数和/或 f 吗? 和标题,所以你的观点没有实际意义。
  • 那为什么要指定浮点类型呢?此外,OP 提供的假设示例都没有涉及可变参数函数。
  • @user4815162342 我猜你没有读过这个问题。如果你这样做了,你就会知道前提是,为什么在最终类型缩进为浮点时指定 .0 ,反之亦然。
  • 没错,最终的类型是浮点数,而在调用printf 时并非如此,必须首先告知给定类型是什么。问题和给定的示例清楚地表明,最终类型在编译时是已知的并已正确声明。
【解决方案3】:

当您需要一个整数值并且整数文字无论如何都会隐式转换为双精度/浮点数时,为什么要使用双精度或浮点文字?

首先,“隐式转换”是矛盾的(根据定义,转换是显式的)。您要查找的表达式是“隐式 [类型] 转换”。

至于为什么:因为它更明确(不是双关语)。如果您对文字的类型有一些视觉指示,这对眼睛和大脑都有好处。

为什么还要添加 f (以制作浮点文字),其中无论如何都会将双精度转换为浮点数?

例如,因为doublefloat 具有不同的精度。由于浮点数很奇怪而且通常不直观,如果您不指定 @ 987654327@手动输入。

【讨论】:

  • 您是否有一个示例,其中从 double 到 float 的隐式转换与使用 f 指定相同的文字不同?我很难相信它会有所不同。
  • @Kevin See #3 here.
  • Croissant 和 @Kevin - #3 there 不是 请求的示例——这只是一个示例,其中 double->float->double 导致的值不同于原来的双,这很容易。您在此处描述的情况要少得多,如果它发生的话——这就是(float)<double literal><double literal>f 不同的地方。这是否会发生可能取决于编译器解析文字的实现;我不确定。
  • @Kevin 这是您的示例:(float)1.0000000596046448 与 1.0000000596046448f 不同。粗略地说,前者是小数->双->浮点数导致向下舍入两次,产生1;而后者直接使用十进制->浮点数,向上舍入到下一个大于 1 的浮点数。
【解决方案4】:

在大多数情况下,只需说出你的意思即可。

例如,你当然可以写:

#include <math.h>
...
const double sqrt_2 = sqrt(2);

并且编译器将在将 int2 转换为 double 之前生成一个隐式转换(注意:不是强制转换),然后将其传递给 sqrt 函数。所以调用sqrt(2) 等价于sqrt(2.0),并且很可能会生成完全相同的机器码。

但是sqrt(2.0) 更明确。对于读者来说(稍微)更明显的是,参数是一个浮点值。对于采用double 参数的非标准函数,编写2.0 而不是2 可能更加更清晰。

你可以在这里使用整数文字,只是因为参数恰好是一个整数; sqrt(2.5) 必须使用浮点文字,并且

我的问题是:为什么要在需要浮点值的上下文中使用整数文字?这样做几乎是无害的,因为编译器会生成一个隐式转换,但是写2 而不是2.0 有什么好处呢? (我不认为节省两次击键是一个显着的好处。)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-06-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多