52

For a while, I've been representing large powers of 10 in constants using scientific notation, just so I don't have to count the zeros. e.g.

#define DELAY_USEC 1e6 

A colleague pointed out that this isn't safe, because it's not an integer and is not guaranteed to always equal 1000000 exactly. Documentation seems to confirm this, but I'm wondering if its true in practicality. Is there any way to safely declare a power-of-ten integer using a shorthand? Is it safe just to cast it to an int in the define?

12
  • you can note in integer the power-of-ten and assume that is one (by nameing convention for example) and just pass it to a true power-of-ten in the execution of the code, that's safe. Commented Jun 24, 2014 at 14:50
  • You are not casting it to an int in the define. If you want an integral constant for type safety, and make it short for brevity, you could use an hexadecimal literal. Commented Jun 24, 2014 at 14:52
  • I guess because since you are defining a float, there's no way to know if that's what it will be exact since floating point numbers have limited precision. Commented Jun 24, 2014 at 14:52
  • 6
    this is not an answer to your question, but remember you can use math when defining constants like static const int SEC_TO_MILLI = 1000; static const int SEC_TO_MICRO = 1000 * SEC_TO_MILLI; this often helps me avoid needing complicated error-prone number literals. Commented Jun 24, 2014 at 14:54
  • Another problem with this is that in every expression it's part of will be evaluated in 'double' precision, which is probably not what you want. Commented Jun 24, 2014 at 14:58

6 Answers 6

30

In theory, no. Neither language specifies how floating point values are represented, or which values can be represented exactly. (UPDATE: apparently, C11 does recommend a representation. C++, and older C dialects, don't).

In practice, yes, for quite a large range of values. Any implementation you're remotely likely to encounter will use a 64-bit IEEE representation for double. This can represent any integer value up to 253 (approximately 9x1015) exactly. It can certainly represent anything representable by a 32-bit integer type.

Sign up to request clarification or add additional context in comments.

5 Comments

C11 5.2.4.2.2 does specify some restrictions. E.g. a double must be able to hold any integer with 10 decimal digits.
@mafso: Interesting, I didn't know that. That means that any modern C compiler should represent any 32-bit integer value exactly. There isn't (yet) such a restriction in C++, unless I missed it.
I don't know about C++, but C actually does specify that floating point values are supposed to use IEEE 754 where possible (C11 Annex F). A conforming implementation is supposed to #define flags to indicate proper support, so this information is available to the programmer.
@Leushenko: Thanks, my C knowledge is a bit old; I didn't know that had been mandated these days.
Actually, for positive powers of 10 you can safely use large values up to 1e22: as surprising as it may be, they are still exactly representable in 53-bit-significand floating-point format (just try printing them with large precision using a decent implementation of printf). This is because log2(5^22)<52, and 1e22==5^22*2^22.
15

You want to use user-defined literals:

constexpr long long operator "" _k(long long l) { return l * 1000; } constexpr long long operator "" _m(long long l) { return l * 1000 * 1000; } 

then you can simple do:

long long delay = 1_m; long long wait = 45_k; 

6 Comments

But you'll want to call them _k and _m, since literal names without leading underscores are reserved. You might also want _M rather than _m to match the SI prefix.
@MikeSeymour you're right, changed answer accordingly
@MikeSeymour - isn't an underscore followed by uppercase a reserved identifier? (Not reserved literal, but generally reserved for the implementation.)
@Mike - hmmm ... but for "normal" reserved symbols, the implementation would be allowed to define a macro _M (as that is reserved) and that would mess up the user code. So that seems weird to be allowed for user def lit.
@MartinBa: Yes, you're right; _M wouldn't be allowed. I shouldn't have suggested that.
|
11

You ask specifically about powers of ten. 1e6 will be exactly one million. You can go up to 1e22 without anything bad happening. However, note that in both C++ and C, 1e6 is a double constant, rather than an integer constant.

Negative powers of ten are a different story. 1e-1 is inexact, as are all lower powers.

8 Comments

C doesn't guarantee this, bit IEEE does
Not all lower powers: 5e-1 is exact.
@JamesKanze: It's also not a power of ten. (I just fixed the text to clarify that I'm talking about powers of ten.)
Where does the value of 1e22 come from? By my reckoning, the smallest unrepresentable integer is 2^53+1, approximately 1e16.
@tmyklebu: Fair enough, I didn't notice that restriction. It might be worth clarifying the answer, in case I'm not the only one that reads it without the full context.
|
4

It seems that gcc assumes a constant defined using scientific notation as a floating point number unless it is cast.

A simple C code shows this:

#include <stdio.h> #define DELAY_USEC_FP 1e6 #define DELAY_USEC_INT (unsigned int) 1e6 int main() { printf("DELAY_USEC_FP: %f\n", DELAY_USEC_FP); printf("DELAY_USEC_INT: %u\n", DELAY_USEC_INT); return 0; } 

On a x86-64 machine, gcc generates this assembly code ($ gcc -S define.c):

[...] ; 0x4696837146684686336 = 1e6 in double-precision FP IEEE-754 format movabsq $4696837146684686336, %rax [...] call printf movl $1000000, %esi [...] call printf movl $0, %eax 

As stated here, 10e15 and 10e22 are the maximum power of ten numbers that have an exact representation in simple and double-precision floating-point format, respectively.

Larger power of ten numbers can not be represented using 32-bit or 64-bit integer types.

Comments

0

You'll never get rounding errors on something less than INT_MAX, since the specification for double sets aside 52 bits for you to use. Your "fractional component" is just your integer, and your "exponent" will be 1, and floating point doesn't struggle with that.

3 Comments

That's not quite what's going on, since that'll give you a non-normalised floating-point number. double has an implied bit, so you can't represent positive integers that way. (Instead you'll get the normalised equivalent of what you said.)
What do you mean? The value in the "exponent" bitrange of the number won't actually be 1, but I believe that the result works out so that the "fraction" will just contain your number. As long as you're under 2^32 (which I assume the OP is, since he would probably be more careful if he had to use larger constants), that should hold.
The significand will contain all but the first bit of your number. The high-order bit is the implied bit.
0

It's really not safe because the compiler will consider it as a floating point number, so the precision is limited to 53 bits instead of 64 bits of integers (long int) for more you can read about the presentation of floating point numbrers

http://en.wikipedia.org/wiki/Floating_point

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.