8

Basically, if I want something like this,

double b = sin(2.2);

but accidentally write something like this,

double b = sin(2.2f);

there is no error or even warning message, even though this clearly leads to a different, inaccurate, and therefore incorrect result. This type of error could be prevented, by forcing the compiler to not do any implicit conversions of float to double. Is there any way to achieve this, be it through a compilation switch (preferably in Visual Studio), some smart macros, or a class which behaves like float/double variables and declares its own operators?

Edit: I am also interested in solving similar problems using operators (such as e.g. double b = 2.2f*2.2f), or assignments (double b=2.2f).

user3768612
  • 101
  • 1
  • 1
  • 4
  • 3
    Although such a class could easily be written, it would never work for standard library functions with `double` parameters when you pass a `float` value: `2.2f` always has type `float`, never any custom class. Since you do list a custom class as an option, can you edit your question to clarify in which cases you do want an error, and in which cases you don't? –  Jun 29 '14 at 18:26
  • That class would have some explicit conversion functions from and to float or double values, so this could be used to overload standard library functions. That should not be too much work in practice, I think, as it would look the same (and could therefore be put in a macro) for many standard library functions. Do you know of any such class? Then I would not need to write it myself... Basically I want an error on any implicit conversion between float and double (and ints as well, if possible, but that is less important). – user3768612 Jun 29 '14 at 18:54
  • 1
    why is this a problem? just put a regex in your build script that checks for raw floating literals. – TemplateRex Jun 29 '14 at 19:47

4 Answers4

5

You can use a type_assert utility.

Example:

#include <cmath>
#include <type_traits>

template<typename T, typename U>
const U& type_assert(const U& u) {
    static_assert(std::is_same<T, U>::value, "...");
    return u;
}

int main() {
    double t = type_assert<double>(std::sin(2.2f));
}

If the expected type is different then it'll give you a compiler error. Chances are the compiler could probably optimise this if it passes though, at least it did in my case with -O3.

Rapptz
  • 20,807
  • 5
  • 72
  • 86
4

You can render the call of sin(float) ambiguous by defining something like

double sin(float x) { abort(); }

Then calls of sin on a float will give you a compile-time error. This works because, in the cmath header, std::sin is an overloaded function with float sin(float), double sin(double), and long double sin(long double) variants. The compiler cannot know whether you wanted ::sin(float) or std::sin(float), so it throws up its hands in confusion.

I wouldn't necessarily recommend leaving this sort of thing around in your codebase, but it can be useful if you're trying to find all instances of a mistake like this and correct them.

tmyklebu
  • 13,915
  • 3
  • 28
  • 57
  • That would only help for function calls, and not for assignments or operators, but it would solve that part of the problem quite well! – user3768612 Jun 29 '14 at 19:04
  • 1
    @user3768612: Yeah. Again, think of this as a diagnostic tool rather than a useful technique. It's like marking a deprecated member of a struct or class 'private' just to see who's calling it. – tmyklebu Jun 29 '14 at 19:06
2

Something like that?

class MyDouble{
public:
  MyDouble(float _f) {throw "only use double!";}
  MyDouble(double _d) : m_data(_d) {}

  operator float() const {throw "conversion to float occurred!";}
  operator double() const {return m_data;}

private:
  double m_data;
};

but yes, you'd have to start using this class in your calls to math functions, making it very ugly:

MyDouble d(3.2);
sin(d);
// or...
cos(MyDouble(2.3));

UPDATE - an example of an operator implementation (binary operator, as unary would have to be part of the class):

MyDouble operator+(const & MyDouble _lhs, const & MyDouble _rhs){
  MyDouble rez(_lhs);
  rez += _rhs;
  return rez;
}

Of course you don't have to implement it that way (through the use of a shortcut operator+=) but this makes code easier to maintain

YePhIcK
  • 5,816
  • 2
  • 27
  • 52
  • Yeah something like that would work. The calls to math functions could probably be overloaded (and I don't need so many of them anyway), so that is not a big problem. But I also want operators... do you know if anyone has done that already? – user3768612 Jun 29 '14 at 19:06
  • If you are talking about "trivial" math operators - they are very easy to implement – YePhIcK Jun 29 '14 at 21:32
  • Having a compile or link time error for `MyDouble(float)` would seem better than a run-time error...? – Tony Delroy Jun 30 '14 at 01:56
1

I just realized you're using a Microsoft product, but I'll leave this answer since it could help someone else.

Have you looked through the compiler switches? I imagine there is a similar option.

OMG! I just searched for help with Visual Studio Options and Warnings! (link)

I'm SO sorry! I didn't know! That is one of the least helpful listings of options and switches I've ever seen! Even more ironic, they've begun scraping StackOverflow for answers and linking back here.

But I did find some clues:

Compiler Warning (levels 3 and 4) C4244 - conversion' conversion from 'type1' to 'type2', possible loss of data

Used thusly:

// Enable this to find unintended double to float conversions.
// warning C4244: 'initializing' : conversion from 'double' to 'float', possible loss of data
#pragma warning(3 : 4244)

GCC/G++ switches (

Why not let the compiler tell you when you accidentally do this?

From the g++/gcc man page:

-Wdouble-promotion (C, C++, Objective-C and Objective-C++ only)
  * Give a warning when a value of type "float" is implicitly promoted to "double".
    ... some verbage clipped ...
  * It is easy to accidentally do computations with "double" because floating-point
    literals are implicitly of type "double".

Adding -Wdouble-promotion to your CXXFLAGS should provide you with compile-time warnings.

There is also -fsingle-precision-constant which prohibits implicit conversion of floats to doubles.

lornix
  • 1,946
  • 17
  • 14
  • 1
    Well, pooh! Looks like that particular warning (`C4244`) is really to find `narrowing` operations, of which __float__ -> __double__ is not, since that's a `widening` operation. Drat. – lornix Jun 30 '14 at 01:03
  • I may use GCC or Clang in the future, so this is useful to know as well. Unfortunately, there is no development environment available for GCC or Clang which has the same level of functionality as Visual Studio, so for many people Visual Studio and its compiler is the better option, all things considered. – user3768612 Jul 16 '14 at 10:15