2

I am writing some numeric code in C++ and I want to be able to swap between using double and float. I have therefore added a #define MYFLT which I can make either a float or a double as needed. However, how do I deal with the various numeric literals. For example

MYFLT someNumber = 1.2;
MYFLT someOtherNumber = 1.5f;

gives compiler warnings for the first line when MYFLT is a float and for the second line when MYFLT is a double. I know this is a trivial example, but there are other cases where I have longer expresions with literals in and floats can end up being converted to doubles then the result back to floats which I think is costing me significant performance. How should I deal with this?

I could do things like

MYFLT someNumber = MYFLT(1.2);
MYFLT someOtherNumber = MYFLT(1.5);

but this is quite tedious. I'm assuming that in that if I do this the compiler is clever enough to just use a float when needed (can anyone confirm that?). What would be better would be if there was a MSVC++ compiler switch or #define that will tell the compiler to treat all floating point literals as floats instead of doubles. Does such a switch exist?

Even when I wrap all my literals as above my code runs 50% slower when I use float rather than double. I was expecting a performance boost through simd type operations, not a penalty!

Phil

Phil Rosenberg
  • 1,597
  • 1
  • 14
  • 22
  • May be interesting https://stackoverflow.com/questions/3426165/is-using-double-faster-than-float. E.g. why do you want to do this? – j.holetzeck Apr 29 '15 at 10:06
  • 2
    Hint: `typedef` might be a better idea than using `#define` – j.holetzeck Apr 29 '15 at 10:13
  • SIMD won't really help if you aren't doing vector calculations, it's not really easy to vectorize random calculation AND have a performance benefit doing so. Also, MSVC will often use double precision instructions for single precision variables. That means sometimes it convert back and forth to double, even if you only have floats. Maybe you can disable this with /fp:fast, but beware of additional side effects. – ElderBug Apr 29 '15 at 10:15
  • I do have some vector calculations, but perhaps my loops are not formatted well enough to allow the vectoriation to happen. I also write out a lot of data to ram ( a few GB ) and expected a speedup by halving that data. I guess this is a case of make sure you profile our code! – Phil Rosenberg Apr 29 '15 at 11:01
  • MYFLT(1.2) will result in double rounding which is not good. You need to append the f suffix to make sure it's correctly rounded to float precision – phuclv Apr 29 '15 at 12:27
  • @ Lưu Vĩnh Phúc I think that the compiler is clever enough to see that float(1.2) is a float literal and optimise away any use of doubles. Certainly in my small fast sin approximation function adding MYFLT() to all the literals enabled this function to be inlined, which was not the case without the MYFLT() – Phil Rosenberg Apr 30 '15 at 18:10

2 Answers2

1

What you'd want is #define MYFLTCONST(x) x##f or #define MYFLTCONST(x) x depending on whether you want a f suffix for float appended.

MSalters
  • 173,980
  • 10
  • 155
  • 350
0

This is a (not quite complete) answer to my own question.

I found that a small function that was called many times (a fast approximation to sin) didn't have its literals cast as MYFLT. The extra computational hit of this also meant that the compiler wasn't inlining it. This function accounted for most of the difference. Some further profiling seemed to indicate that accessing std::vector<float> was slower than std::vector<double> ( I am using [] to do the access if it matters ). Replacing std::vectors with raw fixed sized arrays sped up the double implementation a little and closed the gap significantly for the float implementation. The float version is now only about 10% slower than the double version. But definitely no speed increase due to either RAM access nor vectorization. I guess I need to think more carefully about my loops to get any benefit there.

I guess the conclusion here (yet again) is that the compiler is pretty good at optimising code - it's much better to work with it and do careful profiling than it is to try and do your own blind "optimisations" which might actually have negative effects, like stopping the compiler performing good inlining.

MSalters
  • 173,980
  • 10
  • 155
  • 350
Phil Rosenberg
  • 1,597
  • 1
  • 14
  • 22
  • Profile release builds, not debug builds. I tested `std::vector` versus raw arrays back in 1999 (yes, 16 years ago) and even back then the release builds were **just as fast**. IOW, the abstraction penalty of `std::vector` was zero. It would be a major regression in any serious C++ compiler if an abstraction penalty was reintroduced. Debug builds are excepted because todays vectors do more debug checks – MSalters Apr 29 '15 at 13:18
  • I did profile the release build and definitely got faster execution time with a raw pointer. I know this argument has been had many times before and I don't want to have it here. I think in the past even in release builds I noticed I got checked iterators with MSVC++, but I don't know for sure. There is info at https://msdn.microsoft.com/en-us/library/aa985965.aspx which indicates that in some situations checked iterators are used in release builds but I don't know if this is related. – Phil Rosenberg Apr 30 '15 at 17:00