std::numeric_limits vs INTN_MAX

140 views
Skip to first unread message

Simon Que

unread,
Aug 18, 2015, 4:35:42 PM8/18/15
to Chromium-dev
In base/basictypes.h it says:
// DEPRECATED: Please use std::numeric_limits (from <limits>) instead.

Is there a preference for std::numeric_limits over INTN_MAX from <stdint.h>? I do see the latter being used in Chromium code though.

Simon

Nico Weber

unread,
Aug 18, 2015, 4:39:19 PM8/18/15
to sq...@chromium.org, Chromium-dev
std::numeric_limits<>() causes a static initializer when used in globals for now, so INTN_MAX has to be used in that context. Maybe that's what you're seeing.

--
--
Chromium Developers mailing list: chromi...@chromium.org
View archives, change email options, or unsubscribe:
http://groups.google.com/a/chromium.org/group/chromium-dev

Peter Kasting

unread,
Aug 18, 2015, 4:43:26 PM8/18/15
to sq...@chromium.org, Chromium-dev
On Tue, Aug 18, 2015 at 1:33 PM, Simon Que <sq...@chromium.org> wrote:
Looks like both are used, but the numeric_limits route may be more common.  My bad regex attempts:


Personally I tend to prefer numeric_limits since I dislike macros and like having values with associated types, but it is a bit more verbose.  As Nico mentions, it's not always appropriate either.

I would say in general it's up to the owners/reviewers of a particular area, but if you have no idea which to use, following the directions in that header file (to use numeric_limits) is probably not wrong.

PK

Simon Que

unread,
Aug 18, 2015, 4:44:16 PM8/18/15
to Nico Weber, Chromium-dev
I'm seeing INTN_MAX being used even for local variables. For example:

I just find it strange that base/basictypes.h explicitly recommends std::numeric_limits<>() while remaining silent on INTN_MAX.

Dana Jansens

unread,
Aug 18, 2015, 4:48:14 PM8/18/15
to Simon Que, Nico Weber, Chromium-dev
On Tue, Aug 18, 2015 at 1:41 PM, Simon Que <sq...@chromium.org> wrote:
I'm seeing INTN_MAX being used even for local variables. For example:

I just find it strange that base/basictypes.h explicitly recommends std::numeric_limits<>() while remaining silent on INTN_MAX.

Knowing or remembering all the rules is hard and reviewers will not catch or agree on everything. If you think the macro use there is a problem or is worth worrying about, please send a patch to change it.

Lei Zhang

unread,
Aug 18, 2015, 5:06:23 PM8/18/15
to Simon Que, Chromium-dev
Depending on what you are trying to do, also check out
base::IsValueInRangeForNumericType() and friends from
base/numerics/safe_conversions.h.

On Tue, Aug 18, 2015 at 1:33 PM, Simon Que <sq...@chromium.org> wrote:

Victor Khimenko

unread,
Aug 18, 2015, 5:57:29 PM8/18/15
to sq...@chromium.org, Nico Weber, Chromium-dev
On Tue, Aug 18, 2015 at 10:41 PM, Simon Que <sq...@chromium.org> wrote:
I'm seeing INTN_MAX being used even for local variables. For example:

I just find it strange that base/basictypes.h explicitly recommends std::numeric_limits<>() while remaining silent on INTN_MAX.

Once upon time (in the era before C++11 constexpr) std::numeric_limits<>() was slower than INTN_MAX on Windows. GCC and Clang were always pretty effective with inlining and we are using C++11-aware standard library on Windows thus it makes sense to use std::numeric_limits<>() for non-globals. When we'll have C++-compatible libraries on MacOS and Android it would make sense to use it everywhere.

Ryan Sleevi

unread,
Aug 18, 2015, 7:56:52 PM8/18/15
to Simon Que, Nico Weber, Chromium-dev
On Tue, Aug 18, 2015 at 1:41 PM, Simon Que <sq...@chromium.org> wrote:
I'm seeing INTN_MAX being used even for local variables. For example:

I just find it strange that base/basictypes.h explicitly recommends std::numeric_limits<>() while remaining silent on INTN_MAX.


That's because INTN_MAX wasn't universally available across all the toolchains when basictypes.h was started; MSVC in particular had a bad time with stdint.h.

But that's been fixed, for a while, and over time, the basictypes.h custom types got cleaned up in favour of of their stdint.h equivalents. If you noticed, each of those constants is defined using our _custom_ typedef'd type, not the stdint.h type. That's why it doesn't/didn't directly encourage the use of the INTN_MAX types.

Now that we have stdint.h, those existing constants are arguably there to support existing code that wasn't cleaned up. Several engineers made multiple passes to excise our custom types, but I saw reviewers start to push back on the change (as being mechanical in nature and causing churn).

If you're writing new code, prefer stdint.h, because it's now available and consistently so.
If you're dealing with existing code, arguably you should look to convert the types to the stdint.h types at some point, as well as the stdint.h macros.

In the mean time, it might help to clarify that comment more, or perhaps just directly recommend the INTN_MAX macros as our custom typedefs are now gone and they're just wrappers for the stdint.h types.

Peter Kasting

unread,
Aug 18, 2015, 8:07:33 PM8/18/15
to Ryan Sleevi, Simon Que, Nico Weber, Chromium-dev
On Tue, Aug 18, 2015 at 4:55 PM, Ryan Sleevi <rsl...@chromium.org> wrote:
If you're writing new code, prefer stdint.h, because it's now available and consistently so.

Yes.
 
If you're dealing with existing code, arguably you should look to convert the types to the stdint.h types at some point, as well as the stdint.h macros.

In the mean time, it might help to clarify that comment more, or perhaps just directly recommend the INTN_MAX macros as our custom typedefs are now gone and they're just wrappers for the stdint.h types.

I'm not sure why the availability of stdint.h automatically implies that we should prefer the stdint.h INTx_MAX family of macros over the things in numeric_limits?  It just means those are widely available to use now if people want them.

PK 

Ryan Sleevi

unread,
Aug 18, 2015, 8:25:32 PM8/18/15
to Peter Kasting, Ryan Sleevi, Simon Que, Nico Weber, Chromium-dev
On Tue, Aug 18, 2015 at 5:06 PM, Peter Kasting <pkas...@chromium.org> wrote:
I'm not sure why the availability of stdint.h automatically implies that we should prefer the stdint.h INTx_MAX family of macros over the things in numeric_limits?  It just means those are widely available to use now if people want them.


See Nico's reply. numeric_limits<> is not a guaranteed to be compiler-optimized, and comes with a static ctor overhead.

While C++11's numeric_limits<> finally gets these as constexpr (c.f. 18.3.2.3), as our C++11-in-chromium page ( https://chromium-cpp.appspot.com/ ) notes, constexpr has issues in MSVC2013.

I'm not sure what you mean by "those are widely available to use now if people want to them" - both the INTx_MAX macros and numeric_limits<> are widely available, but the INTx_MAX macros have reliable performance characteristics across all of the Chromium compilers, while numeric_limits<> has a variety of subtleties.

Peter Kasting

unread,
Aug 18, 2015, 8:29:15 PM8/18/15
to Ryan Sleevi, Simon Que, Nico Weber, Chromium-dev
On Tue, Aug 18, 2015 at 5:24 PM, Ryan Sleevi <rsl...@chromium.org> wrote:
On Tue, Aug 18, 2015 at 5:06 PM, Peter Kasting <pkas...@chromium.org> wrote:
I'm not sure why the availability of stdint.h automatically implies that we should prefer the stdint.h INTx_MAX family of macros over the things in numeric_limits?  It just means those are widely available to use now if people want them.


See Nico's reply. numeric_limits<> is not a guaranteed to be compiler-optimized,

According to Victor's reply, numeric_limits<> should be pretty well optimized on all targets right now.
 
and comes with a static ctor overhead.

...when used in globals.

I'm not sure what you mean by "those are widely available to use now if people want to them" - both the INTx_MAX macros and numeric_limits<> are widely available,

Yes, all I meant was that stdint.h is now widely available so INTx_MAX is now widely available.

but the INTx_MAX macros have reliable performance characteristics across all of the Chromium compilers, while numeric_limits<> has a variety of subtleties.

Assuming the above is correct, "causes a static initializer if used in a global" (which numeric_limits values should rarely be used for) seems a bit more minor than "a variety of subtleties" makes it sound.

PK

Ryan Hamilton

unread,
Aug 18, 2015, 11:18:37 PM8/18/15
to Peter Kasting, Ryan Sleevi, Simon Que, Nico Weber, Chromium-dev
On Tue, Aug 18, 2015 at 5:28 PM, Peter Kasting <pkas...@chromium.org> wrote:
Assuming the above is correct, "causes a static initializer if used in a global" (which numeric_limits values should rarely be used for) seems a bit more minor than "a variety of subtleties" makes it sound.

​Am I understanding this correctly that there are two options, INTx_MAX and numeric_limits<>. INTx_MAX has no downsides, and numeric_limits<> has problems in globals (and possibly but maybe not has perf gotchas). It seems like that suggests we should prefer INTx_MAX?​
 

Simon Que

unread,
Aug 18, 2015, 11:44:35 PM8/18/15
to Ryan Hamilton, Peter Kasting, Ryan Sleevi, Nico Weber, Chromium-dev
My original question was more a matter of coding style and whether there's a style preference in Chrome for one or the other. It sounds like while a case could be made for one or the other, there is no official style preference. Personally I am more comfortable using INTx_MAX because it's less syntactically cumbersome than numeric_limits<> and doesn't require an extra include.

However, the comment in base/basictypes.h gives the impression that there is a coding style requirement to use numeric_limits instead of INTx_MAX. If that is indeed not the case (i.e. could go either way), we should update the comment to reflect both options.

Thiemo Nagel

unread,
Aug 19, 2015, 7:46:47 AM8/19/15
to Nico Weber, Simon Que, Chromium-dev
std::numeric_limits<>() causes a static initializer when used in globals for now

I don't get that. Why are globals different than local variables? Or is this just that we don't care (that much) about initializers for locals?

Since you're writing "std::numeric_limits<>()" with brackets at the end, does it make a difference whether the constructor is called or not? On Linux, both of the following compiles to a constant expression:

int a = std::numeric_limits<int>::max();
int b = std::numeric_limits<int>().max();

Victor Khimenko

unread,
Aug 19, 2015, 8:06:36 AM8/19/15
to Thiemo Nagel, Nico Weber, Simon Que, Chromium-dev
On Wed, Aug 19, 2015 at 1:45 PM, Thiemo Nagel <tna...@chromium.org> wrote:
std::numeric_limits<>() causes a static initializer when used in globals for now

I don't get that. Why are globals different than local variables?

Ask GCC authors. Compare:

$ cat test.cc
#include <limits>

int max_val = std::numeric_limits<int>::max();

int foo() {
  return std::numeric_limits<int>::max();
}
$ gcc -O3 -S test.cc -o- | c++filt
.file "test.cc"
.text
.p2align 4,,15
.globl foo()
.type foo(), @function
foo():
.LFB152:
.cfi_startproc
movl $2147483647, %eax
ret
.cfi_endproc
.LFE152:
.size foo(), .-foo()
.section .text.startup,"ax",@progbits
.p2align 4,,15
.type _GLOBAL__sub_I_max_val, @function
_GLOBAL__sub_I_max_val:
.LFB154:
.cfi_startproc
movl $2147483647, max_val(%rip)
ret
.cfi_endproc
.LFE154:
.size _GLOBAL__sub_I_max_val, .-_GLOBAL__sub_I_max_val
.section .init_array,"aw"
.align 8
.quad _GLOBAL__sub_I_max_val
.globl max_val
.bss
.align 16
.type max_val, @object
.size max_val, 4
max_val:
.zero 4
.ident "GCC: (Ubuntu 4.8.4-2ubuntu1~14.04) 4.8.4"
.section .note.GNU-stack,"",@progbits
$ gcc -std=c++11 -O3 -S test.cc -o- | c++filt
.file "test.cc"
.text
.p2align 4,,15
.globl foo()
.type foo(), @function
foo():
.LFB171:
.cfi_startproc
movl $2147483647, %eax
ret
.cfi_endproc
.LFE171:
.size foo(), .-foo()
.globl max_val
.data
.align 4
.type max_val, @object
.size max_val, 4
max_val:
.long 2147483647
.ident "GCC: (Ubuntu 4.8.4-2ubuntu1~14.04) 4.8.4"
.section .note.GNU-stack,"",@progbits

 
As you can see max() is optimized away when used with local variables, but when function is not declared as constexpr it still produces an initializer. When you mix new compiler with old version of libstdc++ that's the result.

Or is this just that we don't care (that much) about initializers for locals?

There are no problems with locals.
 
Since you're writing "std::numeric_limits<>()" with brackets at the end, does it make a difference whether the constructor is called or not? On Linux, both of the following compiles to a constant expression:

int a = std::numeric_limits<int>::max();
int b = std::numeric_limits<int>().max();

Not with globals - see above.

Thiemo Nagel

unread,
Aug 19, 2015, 8:49:29 AM8/19/15
to Victor Khimenko, Thiemo Nagel, Nico Weber, Simon Que, Chromium-dev
Thanks a lot for the detailed explanation! This is very interesting.

Is this a GCC-only problem? (And will we move away from GCC eventually?) From testing with https://gcc.godbolt.org/ it seems that clang doesn't have this issue:

clang 3.5.1:
foo():                                # @foo()
movl $2147483647, %eax       # imm = 0x7FFFFFFF
retq

max_val:
.long 2147483647              # 0x7fffffff

gcc 5.2.0:
foo():
movl $2147483647, %eax
ret
movl $2147483647, max_val(%rip)
ret
max_val:
.zero 4

Cheers,
Thiemo

Victor Khimenko

unread,
Aug 19, 2015, 8:57:53 AM8/19/15
to Thiemo Nagel, Nico Weber, Simon Que, Chromium-dev
On Wed, Aug 19, 2015 at 2:48 PM, Thiemo Nagel <tna...@chromium.org> wrote:
Thanks a lot for the detailed explanation! This is very interesting.

Is this a GCC-only problem?

No, but it's the most acute with GCC. Clang could introduce initializers as well, but that only happens with -O0 or if expression is too complex. GCC creates static initializers in all cases even with aggressive optimizations.

Both work fine with proper C++11-compatible library and we are moving there...

Peter Kasting

unread,
Aug 19, 2015, 1:10:34 PM8/19/15
to Thiemo Nagel, Nico Weber, Simon Que, Chromium-dev
On Wed, Aug 19, 2015 at 4:45 AM, Thiemo Nagel <tna...@chromium.org> wrote:
std::numeric_limits<>() causes a static initializer when used in globals for now

I don't get that. Why are globals different than local variables?

Globals have to be initialized during a special phase at the beginning of program optimization, while locals are initialized when execution reaches them.

This distinction is also why we have rules like "no global variables of non-POD type".

PK 

Peter Kasting

unread,
Aug 19, 2015, 1:11:16 PM8/19/15
to Simon Que, Ryan Hamilton, Ryan Sleevi, Nico Weber, Chromium-dev
On Tue, Aug 18, 2015 at 8:43 PM, Simon Que <sq...@chromium.org> wrote:
My original question was more a matter of coding style and whether there's a style preference in Chrome for one or the other. It sounds like while a case could be made for one or the other, there is no official style preference. Personally I am more comfortable using INTx_MAX because it's less syntactically cumbersome than numeric_limits<> and doesn't require an extra include.

However, the comment in base/basictypes.h gives the impression that there is a coding style requirement to use numeric_limits instead of INTx_MAX. If that is indeed not the case (i.e. could go either way), we should update the comment to reflect both options.

This all sounds accurate to me and I'm fine with the idea of updating that comment.

PK 

Thiago Farina

unread,
Sep 11, 2015, 12:14:33 PM9/11/15
to pkas...@chromium.org, Simon Que, Chromium-dev
If no one beats me, I will try to send a patch to update it as I was hit by this yesterday when working on ipc/.

 


--
Thiago Farina

Thiago Farina

unread,
Sep 11, 2015, 1:27:10 PM9/11/15
to Chromium-dev, pkas...@chromium.org, sq...@chromium.org
Patch to update the comment sent for review.


Bruce

unread,
Oct 9, 2015, 2:59:59 PM10/9/15
to Chromium-dev, pkas...@chromium.org, sq...@chromium.org
On a related note, I found this code in a header file:

const int64 kMaximumLength = std::numeric_limits<int64>::max();

That's a definition of a global-scope variable and since "std::numeric_limits<>() causes a static initializer when used in globals for now" that means that this may cause a static initializer to be created. And because this is in a header file it may cause *multiple* static initializers to be created (the usual const-integral-type elision may not apply when you have a constructor). In VC++ debug builds there are 1,100 of these created. Changing to = INT64_MAX; makes this work much better (preparing CL now).

So remember, variable definitions in header files may be okay if they are of integral type with trivial constructors, but otherwise not. You should prefer putting variable declarations in header files.

The 'of integral type' requirement is off-topic but also important - see crbug.com/541668 which causes 2,473 copies of a 256 byte array to show up in VC++ 2015 debug builds. Your mileage may vary.
Reply all
Reply to author
Forward
0 new messages